修改docker容器端口映射

在docker run创建并运行容器的时候,可以通过 -p 指定端口映射规则。但是,我们经常会遇到刚开始忘记设置端口映射或者设置错了需要修改。当docker start运行容器后并没有提供一个-p选项或设置,让你修改指定端口映射规则。那么这种情况我们该怎么处理呢?今天Docker君教你如何修改运行中的docker容器的端口映射? 方法一:删除原有容器,重新建新容器 这个解决方案最为简单,把原来的容器删掉,重新建一个。当然这次不要忘记加上端口映射。 优缺点: 优点是简单快捷,在测试环境使用较多。缺点是如果是数据库镜像,那重新建一个又要重新配置一次,就比较麻烦了。 方法二:修改容器配置文件,重启docker服务 容器的配置文件路径: /var/lib/docker/containers/[hash_of_the_container]/hostconfig.json 其中的 hashofthecontainer 是docker镜像的hash值,可以通过 docker ps 或者 docker inspect containername 查看。(CONTAINER ID就可以看出来) 文件中其中有一项是PortBindings,其中8080/tcp对应的是容器内部的8080端口,HostPort对应的是映射到宿主机的端口9190。8361/tcp对应的是容器内部的8361端口,HostPort对应的是映射到宿主机的端口9191。按需修改端口,然后重启docker服务,再启动容器服务就可以了。 systemctl restart docker 优缺点: 这个方法的优点是没有副作用,操作简单。缺点是需要重启整个docker服务,如果在同一个宿主机上运行着多个容器服务的话,就会影响其他容器服务。 方法三:利用docker commit新构镜像 docker commit:把一个容器的文件改动和配置信息commit到一个新的镜像。这个在测试的时候会非常有用,把容器所有的文件改动和配置信息导入成一个新的docker镜像,然后用这个新的镜像重起一个容器,这对之前的容器不会有任何影响。 1、停止docker容器 docker stop container01 2、commit该docker容器 docker commit container01 new_image:tag 3、用前一步新生成的镜像重新起一个容器 docker run --name container02 -p 80:80 new_image:tag 优缺点: 这种方式的优点是不会影响统一宿主机上的其他容器,缺点是管理起来显得比较乱,没有第二种方法那么直观。

2020-01-20 · 1 min · 52 words · Hank

Docker入门系列八——使用Dockerfile构建镜像

Dockfile就是一个文本文件,里边包含了一行行的指令,用来描述如何创建自定义镜像。 1. 使用 使用 docker build 命令来基于Dockerfile文件和上下文构建镜像,构建上下文指的是特定路径(PATH或URL)的文件集合,PATH用来指定本地文件系统目录,而URL用来指定Git仓库的地址,它们包含的所有文件(子目录或子模块)都会被递归处理。在大多数情况下,最好以空目录作为上下文,并将Dockerfile保存在该目录中。仅添加构建Dockerfile所需的文件。 docker build命令语法: docker build [OPTIONS] PATH | URL | - 例如: $ docker build . Sending build context to Docker daemon 6.51 MB ... 上边的命令会被docker daemon程序来处理而不是docker CLI,构建进程将发送指定路径下的Dockerfile文件到docker daemon。 Dockerfiie通过命令来关联构建上下文的文件,如 COPY、ADD 等命令。另外,可以通过 .dockerignore文件来排除文件或目录,以提高构建速度和性能。 一般而言,Dockerfile文件应该位于构建上下文的根目录中,但是,也允许使用 -f 选项来指定它的位置: $ docker build -f /path/to/a/Dockerfile . 也可以在镜像构建完成后指定tag: $ docker build -t shykes/myapp . 多次使用 -t 选项可以指定多个tag。 Docker daemon运行Dockfile之前会先进行校验,校验失败会输出错误信息: $ docker build -t test/myapp . Sending build context to Docker daemon 2.048 kB Error response from daemon: Unknown instruction: RUNCMD ...

2019-11-15 · 4 min · 711 words · Hank

使用Springboot开发websocket程序(四)——使用RabbitMQ作为STOMP消息代理

上一篇,我们在介绍了Spring中如何使用websocket的子协议stomp,并使用简单的基于内存的stomp消息代理来编写了一个web聊天室实例。基于内存的stomp消息代理,虽然能够满足基本需求,但还是存在一些不足,比如由于stomp代理在应用内部,多个外部websocket应用需要消息互通,那么就难以满足了。在本篇,我们来学习如何使用RabbitMQ作为stomp代理。 1. 为何要使用外部消息代理 简单消息代理,能够满足单websocket应用的需要,但是如果有多个websocket应用,他们之间需要进行消息共享,那么就需要做大量的工作才能实现了。其实,MQ一个最重要的作用就在于能个在各个系统间解耦。引入外部MQ作为stomp消息代理,很好的解决了多系统消息共享的问题,只要其支持stomp协议。RabbitMQ本身提供了对STOMP的支持,加上后结构变化如下: 前边的是单应用时的结构,后边为怎么了RabbitMQ过后,多个应用程序结构。 2. RabbitMQ对STOMP的支持 RabbitMQ对stomp协议的支持是通过插件的方式,默认stomp插件是关闭的,我们需要先启用之。 2.1. 启用插件 进入rabbitmq所在服务器,然后控制台输入如下命令来启用stomp插件: ``rabbitmq-plugins enable rabbitmq_stomp`` 然后可以查看插件是否启用成功: ``rabbitmq-plugins list`` 2.2. 插件配置 默认情况下,STOMP将会监听61613端口,默认的用户名和密码都为guest。通过配置文件来配置: ubuntu下rabbitmq的配置文件在/etc/rabbitmq/rabbitmq.conf,找到stomp开头的选项,就可以进行配置了 比如配置STOMP监听端口: ``stomp.listeners.tcp.1 = 12345`` RabbitMQ中STOMP适配器连接时如果用户名和密码使用默认的guest/guest,则可以忽略,如果需要修改,则配置如下: stomp.default_user = guest stomp.default_pass = guest 2.3. Destinations STOMP规范并没有规定消息代理来支持什么样的目的地(destination),只是根据消息头的destination的值来判断消息发送的目的地,一般由消息代理自定义支持,RabbitMQ中定义了几种destination类型: #exchange[/exchange]: 发送到任意的routing key和订阅任意的binding key #queue[/queue]: 发送和订阅队列,该队列由STOMP管理 #amqqueue[/amq/queue]: 发送和订阅外部创建的队列 #topic[/topic]: 发送和订阅到topic #temptopic[/temp-queue/]: 创建临时的队列(使用reply-to请求头) 现在,我们结合代码来看看Spring中对RabbitMQ的这几类destination是如何支持的。 3. Spring中使用RabbitMQ消息代理 我们通过一个demo来看看如何在Spring中使用RabbitMQ支持的这几个destination,整体界面如下; 下边的示例仅贴上部分关键代码,完整的代码可以参看文末的源码。 首先,我们创建一个名为03-websocket-stomp-rabbitmq的springboot工程,引入如下依赖: <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-net</artifactId> <version>2.0.5.RELEASE</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.0.33.Final</version> </dependency> <dependencies> ...

2019-11-12 · 3 min · 509 words · Hank

使用Springboot开发websocket程序(三)——基于子协议STOMP的web聊天室

我们使用Spring Boot对原生WebSocket的支持来编写了一个简单的聊天室程序。它仅支持群发,如果要做到点对点消息,我们还需要编写代码自己实现。本篇,我们将使用websocket的子协议STOMP来更新我们的聊天室程序,使其可以支持单聊。 1. STOMP 1.1. 为什么要支持子协议 WebSocket是一种简单的通信协议,而并非消息协议。它是TCP之上的非常薄的一层,所做的事情仅仅是将字节流转换为消息流(文本或二进制)。至于消息是什么含义,怎么路由和解析,都交由应用程序自身来决定。HTTP协议是一种应用级协议,它明确告诉我们请求的地址、格式、编码等等信息,但是websocket与之不同,websocket并不提供详细的信息来告诉我们如何路由和处理消息。因此,对于开发大型应用程序而言,websocke级别太低了,要实现非常复杂的功能,我们需要进行大量的编码工作。这就好比,大多JAVA WEB开发都会选择使用Spring框架,而不是基于Servlet API来实现。 基于这个原因,WebSocket RFC定义了 子协议的使用规范。在握手阶段,客户端和服务端使用Sec-WebSocket-Protocol请求头来通知彼此使用子协议,即更高级的、应用级的协议。当然,也可以不使用子协议,但是客户端和服务端仍然需要定义消息的格式。使用更规范的通用消息协议,更能让应用程序开发和维护变得简单。STOMP就是这样的一个消息协议,Spring框架提供了对其的支持。 1.2. 什么是STOMP STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,中文称简单(流)文本定向消息协议,它是一种简单的互操作协议**,旨在通过消息代理在客户端之间传递异步消息,STOMP协议具有简单行和互操作性,因此在多种语言和多种平台上得到广泛地应用。。 STOMP是由于需要通过脚本语言(例如Ruby,Python和Perl)连接到企业消息代理而产生的,旨在对消息进行简单的消息处理,例如可靠地发送单个消息并断开连接或在给定目的地上消耗所有消息等等。它是其他开放消息协议(例如AMQP)和JMS代理(例如OpenWire)中使用的实现特定wire protocol的替代。STOMP仅仅实现部分常用的消息API,而不是实现完整的消息API,因此更加轻量和简单。 STOMP除了支持文本消息外,也支持二进制消息,默认使用UTF-8的编码格式。 STOMP有几个比较重要的概念: frame:帧,即STOMP客户端和服务端发送的数据 COMMAND: frame的命令,由STOMP规范定义,用来表示消息的特定用途,比如连接服务端、发送消息等,每个frame都有命令 destination:消息发送的目的地址,通过消息头来表示,如destination:/topic/chat STOMP基于frame,frame的模型源于HTTP,一个frame由命令、一组可选的标头和可选的主体组成,客户端和服务端发送frame来进行通信。frame的格式如下: COMMAND header1:value1 header2:value2 Body^@ frame分为两个部分:消息头和消息体。消息头第一行为命令,然后跟key:value格式的头信息;然后是消息体,消息体跟消息头之间用一个空行分隔。最后,消息体后跟八位空字节(上文用^@表示)。另外,frame的命令和消息头信息都区分大小写。 STOMP客户端支持的命令包括:SEND、SUBSCRIBE、UNSUBSCRIBE、BEGIN、COMMIT、ABORT、ACK、NACK、DISCONNECT、CONNECT、STOMP 服务端支持的命令包括:CONNECTED、MESSAGE、RECEIPT、ERROR 只有几种消息可以包含消息体:SEND、MESSAGE、ERROR 举个例子,客户端连接到服务端,则会想服务端发送如下的frame: CONNECT accept-version:1.0,1.1,2.0 host:stomp.github.org ^@ 服务端接收连接请求则会发送下边的frame给客户端,否则发送ERROR frame: CONNECTED version:1.2 ^@ 1.3. 使用STOMP的优点 使用STOMP作为子协议,与使用原生WebSocket相比,Spring框架和Spring Security可以提供更丰富的编程模型。具体的优点有以下几点: 无需自定义消息协议和消息格式 可以使用STOMP客户端,包括Spring框架中的Java客户端 消息代理(例如RabbitMQ,ActiveMQ和其他代理)可以用于管理订阅和广播消息 可以在任意数量的@Controller中组织应用程序逻辑,并根据STOMP消息头将消息路由给它们,而对于给定的连接,可以使用单个WebSocketHandler处理原始WebSocket消息 可以使用Spring Security基于STOMP 目标(destinations)和消息类型来对消息进行安全处理 详细内容请看STOMP规范: https://stomp.github.io/stomp-specification-1.2.html 2. Spring Boot中使用STOMP 使用STOMP有如此多的好处,我们看看Spring中如何使用。 在Spring boot中使用STOMP,大概需要以下几步: ...

2019-11-02 · 5 min · 914 words · Hank

使用Springboot开发websocket程序(二)——基于原生websocket的web聊天室

上一篇介绍了什么是websocket,说到websocket是一种由HTML5定义的浏览器和服务器保持长连接的通信协议,可以进行实时数据交换。在本篇,我们将使用Spring boot,基于原生websocket开发一个web聊天室,并一步步介绍如何在spring boot中开发websocket程序。 一个WebSocket程序包括客户端和服务端。WebSocket客户端除了支持Html5的浏览器外,还包括各大语言提供的WebSocket实现,比如Java中Spring框架的实现,从而在没有浏览器时也能进行websocket通信。HTML5中WebSocket API请看 这里。服务端中,Java定义Java WebSocket API标准 JSR-356[JSR-356],Java主流容器都已经支持websocket,主要包括Tomcat 7.0.47 ,Jetty 9.1 +,GlassFish 4.1 +,WebLogic 12.1.3+和Undertow 1.0(以及WildFly 8.0+)等。 1. 整体功能 现在我们来写一个简单的web的聊天室程序,并一步步学习Spring中是如何封装WebSocket的,这里工程还是使用Spring Boot。 整体功能界面如下: 功能很简单:用户填写上自己的用户名,然后点击链接按钮进入聊天室,然后就可以发送消息给其他人,聊天室中的用户可以看到他人的连入信息和发送的消息。 我们看看在spring boot中如何编写以上程序。 2. 服务端 1、新建一个springboot-websocket的maven工程,引入如下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> 引入该启动器后,Spring boot会自动引入一下几个启动器或jar包: spring-boot-starter:spring boot启动器 spring-boot-starter-web:spring boot web工程启动器 spring-messaging:包含对消息传递应用程序的基础支持,下一篇用到的STOMP协议的支持也在这个模块。它是Spring Integration项目中的关键抽象,包含Message,MessageChannel,MessageHandler和其他可以作为此类消息传递体系结构基础支撑 spring-websocket: Spring对websocket的支持,它与Java WebSocket API标准( JSR-356[JSR-356])兼容,并且还提供了很多扩展 由此可见,对websocket的支持在spring-websocket和spring-messaing两个模块。 2、编写启动类: @SpringBootApplication public class SpringWebsocketApplication { public static void main(String[] args) { SpringApplication.run(SpringWebsocketApplication.class, args); } } 上边的两步没什么说的,常规的spring boot工程开发。 3、编写websocket握手拦截器: ...

2019-11-01 · 3 min · 577 words · Hank

使用Springboot开发websocket程序(一)——什么是websocket

在互联网飞速发展的当代,浏览器和服务器之间的实时通信已经越来越重要,传统的HTTP协议难以解决实时通信的需求。因此,由HTML5定义的websocket协议应运而生。这里我将用三篇文章来介绍websocket,并使用Springboot开发一个web聊天室的程序,其中会使用原生websocket协议开发,也会单独来介绍使用STOMP协议来开发。 在本篇,我们先看看什么是websocket。 1. 诞生背景 由于HTTP是无状态的协议,采用请求响应模型,服务端是不能主动推送信息给客户端的,只能由客户端发起请求,然后再由服务端进行响应。如果服务端数据有变化,必须通过客户端来获取,服务端是不能主动推送的。要解决实时交换数据的需求,一般的做法是通过轮询(还有http long pull和streaming两种方案,参见这篇 博文)来获得服务端最新信息,即:客户端(浏览器)每隔一段时间(比如1秒)向服务端发起请求,以获取最新的数据。 我们知道,HTTP是一种无状态协议,客户端请求然后服务端响应,则会断开TCP连接。因此,使用轮询的弊端是,客户端不断与服务端建立连接、断开连接,效率非常低下,且非常消耗服务器资源,并且也不是真正意义上的实时。 因此,我们需要一种技术,能够让服务端主动推送数据给客户端,而且消耗资源很少,效率更高。 2. 什么是websocket WebSocket是一种在 单个TCP连接上进行全双工通信的协议,2008诞生,在2011年被IETF定为标准 RFC6455,并由 RFC7936补充规范。WebSocket API也被W3C定为标准,目前各大主流浏览器都已经支持WebSocket。 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据,这些数据通常包括两种:文本和二进制。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。 3. 客户端与服务器的握手 WebSocket基于Http协议来完成握手(Handshake)阶段,在该阶段,客户端需要将服务端请求协议升级,由原来的HTTP协议升级为WebSocket协议,如果服务端能够支持WebSocket,则会告诉客户端升级成功。之后,双方就建立了一条快速通道,可以互相进行数据交互,后续通信就采用WebSocket协议来完成。 Figure 1. websocket与http的区别(图片来源于网络 我们来简单看一下websocket与服务端的握手过程。WebSocket请求头跟HTTP请求头类似,只是添加了一些特殊的头标签,一个握手请求的头信息如下: GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com 可以看到,websocket在握手阶段,采用的仍然是HTTP协议,但是请求添加了一些websocket特有的信息: Upgrade: websocket:就是在请求服务端升级通信协议为WebSocket Sec-WebSocket-Key:浏览器随机生成的一个base64的加密字符串,服务器用它来进行校验请求是来自websocket客户端 Sec-WebSocket-Protocol: 告诉服务器不直接使用websocket,而是使用websocket协议的子协议,例如后文将要介绍的STOMP就是websocket协议的子协议来通信 Sec-WebSocket-Version: 使用websocket协议的版本号 请求发送到服务器,如果服务器也支持websocket则升级成功,并响应如下头信息: HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat 这里,websocket握手完成服务器则会响应101状态码,至此,HTTP协议服务就已经结束,后续通信就采用websocket了。 4. websocket的应用场景 浏览器需要与服务端进行实时数据交换时,都可以使用websocket协议。最常见的如视频直播网站,都有聊天室和弹幕功能,巨大的数据量,如果使用HTTP,交过可想而知。另外,网页游戏开发、也包括一些电商类网站的秒杀、团购等等场景,都会用到websocket。 5. websocket备选方案 目前,各大浏览器都已经支持websocket,如果仍要兼容老版本的浏览器,或者某些代理服务对websocket协议有一些限制,又或者由于连接保持打开时间过长而被浏览器强制断开,导致出现问题,该怎么办呢? ...

2019-11-01 · 1 min · 98 words · Hank

使用Redis实现分布式锁

在单体Java应用中,由于代码运行于同一个JVM,使用实现资源加锁是比较容易的,例如使用synchronized或ReentrantLock加锁来控制并发访问;但是在分布式系统中,多个分布式系统之间也需要控制并发访问,由于处于不同的JVM,此时就不能简单使用java的锁机制来进行控制。这种跨进程或者跨服务器的加锁,需要额外使用全局的获取锁的服务,就是本文探讨的分布式锁。 1. 为什么需要分布式锁 分布式锁解决的问题:保证分布式系统的共享资源在某一时刻只被一个客户端访问,保证数据的准确性。 举个例子: 如图所示,订单服务下单前需要保证库存足够,库存服务首先会检查库存充足,然后在将订单的库存数量锁定,如果此时管理系统对库存数量进行了修改,那么由于跨系统的并发操作可能操作库存数据的不正确。此时,对库存的操作就需要考虑分布式锁,将库存锁定,暂时不能更改。 这样一来,对库存的更改和扣减操作使用同一把锁来锁定,每次只有一个客户端能够操作成功,要么订单服务先扣减服务,要么管理系统先修改库存,反正两个不能同时进行。 2. 分布式锁的现有方案 分布式锁的整体思路是:在分布式系统中,有一个全局的东西(中间件或服务),各个服务需要加锁时都向它获取锁,然后给锁一个标记(例如锁的名称),如果标记相同则认为是同一把锁,这样就可以控制各个系统的资源共享。 目前,分布式锁的方案大致有以下几种: 基于Zookeeper的临时节点 基于Redis的SET命令 这里仅仅讨论Redis的分布式锁实现。 3. Redis实现分布式锁的原理 基于Redis来实现分布式锁,其原理很简单:在Redis中设置一个Key,表示加锁,如果其他系统来加锁时发现这个Key已经存在,表示已经加了锁,则它获取锁失败,然后它再不断重试加锁,直到加锁成功,然后才能执行后续业务逻辑。释放锁也很简单,直接将这个KEY删除即可。 锁一旦被创建,就必须能够释放,否则会引起死锁(其他系统永远获取不到锁),一般会使用Redis的过期机制,让KEY一段时间后自动过期以避免死锁。 加锁时,过程如下: 首先,使用SET命令来为某一个KEY(可以作为锁名称)设置一个唯一的值,仅当KEY不存在时才能加锁成功,如果KEY存在则设置失败,表明锁已经存在; 其次,为该KEY设置一个过期时间,来避免死锁问题; 释放锁时,先获取锁是否存在,如果存在则调用DEL命令删除该KEY。 无论是加锁,还是释放锁,都需要保证命令的原子性执行(要么都成功,要么都失败,试想一下,如果加锁时SET命令成功,然后在调用EXPIRE命令设置过期时间,未完成时Redis宕机了,会造成死锁)。例如,加锁时,SET命令和设置过期时间需要为一个原子命令,Redis已经提供了原子命令,如下: // NX是指如果key不存在就成功,key存在返回false,PX指定过期时间,单位毫秒 SET anyLock unique_value NX PX 30000 释放锁时,获取锁和删除KEY为一个原子操作,Redis没有提供获取KEY然后DEL的原子命令,这里需要用到LUA脚本以保证原子性: // 执行LUA脚本保证原子性,先获取锁,然后调用DEL删除 if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end 需要注意的是,加锁时设置的KEY值value必须是唯一的,这是因为在释放锁时需要获取到该值以便验证释放锁的客户端和加锁的客户端是同一客户端,如果value值不唯一则可能客户端A加了锁,但是由客户端B给释放了,引起业务混乱而没有达到加锁的目的。 4. 三种部署方式下的锁问题 Redis有三种部署方式,每种方式下的分布式锁都存在一些问题: 1、单机部署 这种方式下,很明显的缺点就是单点问题,Redis故障了,那么分布式锁就不能使用了。 2、Master-Slave + Sentinel模式 主从+哨兵模式,主节点挂了,哨兵会重新选择一个从节点作为主节点,数据会复制到从节点上,但是复制过程需要一定的时间,如果主节点挂了,它上边的锁可能还没有复制到从节点上,就会造成锁丢失。 3、Cluster模式 集群部署模式,同理,在某一个节点上的锁可能还没有复制到其他节点上,同样会造成锁丢失。 使用Redis的分布式锁,其优点是性能很高,支持高并发分布式锁场景,缺点则是如果加锁失败,需要不断循环重试加锁,消耗资源,另外,Redis集群下可能造成锁丢失的极端情况,对于这种情况,Redis的作者也考虑到了,他提出了RedLock算法,具体可以看 这里。 5. 使用Redisson的分布式锁 一般而言,不推荐自己实现Redis分布式锁,因为需要考虑诸如锁重入等多种情况,Java的Redisson框架已经为我们提供了分布式锁的支持。 Redisson是一个Java版的Redis Client,提供了大量的基于Redis的分布式特性支持,例如 分布式锁、分布式任务调度、分布式远程服务、分布式集合等等,Redisson官网: https://redisson.org/ 要使用Redisson的分布式锁非常简单,基本的代码如下: RLock lock = redisson.getLock("anyLock"); lock.lock(); // do something …… lock.unlock(); ...

2019-10-17 · 2 min · 392 words · Hank

IntelliJ IDEA 2019.2.x激活

IntelliJ IDEA 2019.2.x版本已经可以成功激活了,理论上此种激活方式适合2019的所有版本,不过笔者仅测试了2.3版本。具体激活步骤如下: 1、下载最新版本的idea, 目前版本为2019.2.3 2、下载用于激活的代理压缩包,下载地址如下: 链接 https://pan.baidu.com/s/1bsG4E9P744VlgG_kwgZ40w 密码 z98a 3、解压缩下载的代理包,得到jetbrains-agent.jar,把它放到你认为合适的文件夹内 4、启动IDE,如果需要注册,选择:试用(Evaluate for free)进入IDE 5、点击你要注册的IDE菜单:"Configure" 或 "Help" → "Edit Custom VM Options …​" 如果提示是否要创建文件,请点"Yes",在打开的vmoptions编辑窗口末行添加: -javaagent:/absolute/path/to/jetbrains-agent.jar 一定要自己确认好路径(不要使用中文路径),要使用绝对路径,填错会导致IDE打不开! 一个vmoptions内只能有一个-javaagent参数。 示例: mac: -javaagent:/Users/neo/jetbrains-agent.jar linux: -javaagent:/home/neo/jetbrains-agent.jar windows: -javaagent:C:\Users\neo\jetbrains-agent.jar 如果还是填错了,参考这篇文章编辑vmoptions补救: https://intellij-support.jetbrains.com/hc/en-us/articles/206544519 注意 这是最重要的一步,不要直接在IDEA目录下修改配置文件,而是在IDEA启动后通过菜单来修改,否则可能不能成功激活** 6、重启你的IDE 7、点击IDE菜单 "Help" → "Register…​" 或 "Configure" → "Manage License…​" 支持两种注册方式:License server 和 Activation code: 1). 选择License server方式,地址填入:http://jetbrains-license-server (可能会自动填上) 或者点击按钮:"Discover Server"来自动填充地址。 2). 选择Activation code方式离线激活,请使用压缩包内的ACTIVATION_CODE.txt中的注册码激活 OK,激活成功! 代理包连接失效请留言,我会第一时间补上! 此项目仅供交流学习用,请勿用于商业用途,若资金允许,请购买正版!

2019-10-12 · 1 min · 65 words · Hank

Docker入门系列七——端口映射与容器互联

1. 端口映射 1.1. 外部访问容器内的应用 如果外部要访问容器,需要将容器的端口开放出来,使用 -p 或 -P (大写)参数来映射容器端口和宿主机端口的关系: -p:明确指定端口映射,每个端口只能绑定一个容器,格式包括:IP:HOST_PORT:CONTAINER_PORT|IP:CONTAINER_PORT|HOST_PORT:COINTAINER_PORT 举例: docker run -p 8080:80 nginx 该命令表示启动容器是将宿主机的8080端口映射为容器的80端口,外部访问宿主机的8080端口就是在访问容器的80端口。 -P(大写):随机映射一个宿主机的可用端口到容器 举例: docker run -P nginx 通过docker ps命令可以看到端口的映射结果: root@ubuntu:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9e4cb770c5cc nginx "nginx -g 'daemon of…" 17 seconds ago Up 16 seconds 0.0.0.0:32768->80/tcp peaceful_robinson 可以看到,宿主机的32768端口映射到容器的80端口 1.2. 映射多个端口 多次使用 -p 参数,可以将容器的多个端口与宿主机的多个端口进行映射 例如: docker run -p 8080:80 -p 8081:80 nginx 1.3. 映射指定地址的指定端口 使用 IP:HOST_PORT:CONTAINER_PORT 格式来指定IP地址下的端口 例如: ...

2019-09-15 · 2 min · 234 words · Hank

SpringBoot自定义日志配置

当系统出现问题时,先从表现分析问题远远不够,要快速、准确地分析并定位问题原因,日志就是一个非常重要且必不可少的手段。Java而言,日志框架有很多,常用的有Common-logging、Java自带的Logging、Log4J、Logback等。另外,还有一个Slf4J的框架,主要目的是将这些框架进行整合。本篇主要介绍的是SpringBoot中的日志支持,如何进行日志自定义。 1. 日志框架选择 在SpringBoot内部,使用common-logging框架来记录日志,也支持自定义配置,支持的框架有:Java util logging(JDK自带)、Log4J和Logback等。 如果使用了Springboot的starter,则springboot会使用Logback作为默认的日志框架,从maven的依赖关系可以看到,spring-boot-starter下依赖的spring-boot-starter-logging启动器使用了logback: <dependencies> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> </dependency> </dependencies> 如上所示,Spring boot内部使用Slf4J框架,作为日志抽象层,以便整合并支持各大主流的框架,具体日志实现还是使用logback。 2. 日志输出内容 一个标准Spring boot日志如下图所示: 其实,Spring boot工程默认的日志级别为INFO,仅输出INFO级别的日志信息,由于我这里开启了debug模式(配置debug=true),所以能打印出DEBUG级别的日志级别。 一般而言,日志有几个重要的输出项: 时间:记录日志时的时间,精确到毫秒,便于排序,缩小问题范围的重要指标,例如问题产生在某天10:20,那么就需要查找这段时间的所有日志以帮助分析问题 线程:输入日志的执行线程名称 日志级别:日志的输出级别,不同的日志框架级别定义不同,而且日志级别有层级之分,一般而言,日志级别从低到高都有调试(DEBUG)、信息(INFO)、警告(WARNING)、错误(ERROR)等,不同的框架可能名称不同,日志级别越高,输出的信息越少,反之则越多 进程ID:记录输出日志的进程ID 类名:输出日志的类名称,通常是简写的,例如:c.b.WebappViewStarter 日志内容:具体的日志内容 如果输出终端支持ANSI字符集,那么日志会彩色显示,可以通过配置spring.output.ansi.enabled的值来设置彩色显示的条件,包括ALWAYS、DETECT、NEVER。日志的颜色可以通过在日志输出格式时定义%clr来配置,支持的颜色有:blue、cyan、faint、green、magenta、red、yellow,例如: ``%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){yellow}`` 输出的日志级别默认为INFO,可以通过在配置文件修改来自定义日志级别: logging.level.root=WARN logging.level.org.springframework.web=DEBUG logging.level.org.hibernate=ERROR root配置整个日志输出的级别,没有特别配置均按照此级别输出,而下边两项则是配置具体某一个框架的日志级别。 3. 输出日志到文件 默认情况下,SpringBoot仅将日志输出到控制台,但我们更希望将日志输出到单独的文件,此时我们需要在配置文件中添加logging.file或者logging.path配置项,他们的作用如下: logging.file:配置一个具体的日志文件的路径,可以是绝对或者相对路径,建议定义为绝对路径,避免不需要的路径问题,例如logging.file=/data/logs/springboiot-demo.log, logging.path:配置一个存储日志的目录,会在该目录下创建spring.log文件,并写入日志内容,如:logging.path=/var/log 需要注意的是,二者不能同时使用,如若同时使用,则只有logging.file配置生效。默认情况下,日志文件的大小达到10MB时会重新创建新文件来写入日志,原日志文件会被顺序重新命名,例如:springboot-demo.log、springboot-demo.log1,默认输出的日志级别为:ERROR、WARN、INFO。 4. 自定义日志 Springboot虽然可以成功将日志输出到自定义的文件中,但是通常,我们更希望按照日期来输出日志文件,例如每天一个日志文件,保留一定天数的日志文件。Springboot也支持此类特殊需求的自定义日志,只需要提供一个自定义日志配置文件即可,该配置文件可以通过logging.config配置项来定义,例如:logging.config=classpath:logging-config.xml,但是,一般不需要我们配置,只需要使用springboot能加载的日志配置文件名即可,springboot默认可以加载下表的这些配置文件: 日志框架 自定义日志配置文件 Logback logback-spring.xml, logback-spring.groovy, logback.xml 或 logback.groovy Log4j2 log4j2-spring.xml or log4j2.xml JDK (Java Util Logging) logging.properties 例如,我们要使用默认的logback日志框架,并且日志要按天输出,我们只需在resources目录下添加一个logback-spring.xml文件,加入如下内容: ...

2019-09-06 · 1 min · 182 words · Hank

Mysql binlog格式

在前一篇,我们开启了Mysql的binlog,当Mysql在进行数据更改操作时会自动记录binlog日志,以便进行主从服务,或者恢复丢失的数据。Binlog的日志内容可以设置不同的格式,以满足不同的需求。 1. Binlog格式 Binlog有三种格式(format),包括STATEMENT、ROW和MIXED,根据Mysql版本的不同,有着不同的默认值,它们的设置方式和默认值见下表: 属性 值 命令行格式 --binlog-format=format 系统变量 binlog_format 范围 GLOBAL, SESSION 动态 是 类型 列举 默认值(>= 5.7.7) ROW 默认值(⇐ 5.7.6) STATEMENT 有效值 ROWS TATEMENT MIXED 1.1. STATEMENT 基于SQL语句的日志记录,每一条修改数据的sql语句都会被记录在binlog中,但是不会记录数据前后的变化。 优点 只需要记录执行语句的细节和上下文环境,避免了记录每一行的变化,在一些修改记录较多的情况下相比ROW level能大大减少binlog日志量,节约IO,提高性能;还可以用于实时的还原;同时主从版本可以不一样,从服务器版本可以比主服务器版本高 缺点 为了保证sql语句能在slave上正确执行,必须记录上下文信息,以保证所有语句能在slave得到和在master端执行时候相同的结果;另外,主从复制时,存在部分函数(如sleep)及存储过程(procedure、function、及trigger)在slave上会出现与master结果不一致的情况,而相比Row level记录每一行的变化细节,绝不会发生这种不一致的情况 1.2. ROW 基于行的日志记录,不记录SQL语句,仅记录被修改的行的数据信息。 优点 能非常清晰的记录下每行数据的修改细节,不需要记录上下文相关信息,因此不会发生某些特定情况下的procedure、function、及trigger的调用触发无法被正确复制的问题,任何情况都可以被复制,且能加快从库重放日志的效率,保证从库数据的一致性 缺点 由于所有的执行的语句在日志中都将以每行记录的修改细节来记录,因此,可能会产生大量的日志内容,干扰内容也较多;比如一条update语句,如修改多条记录,则binlog中每一条修改都会有记录,这样造成binlog日志量会很大,特别是当执行alter table之类的语句的时候,由于表结构修改,每条记录都发生改变,那么该表每一条记录都会记录到日志中,实际等于重建了表。 row模式生成的sql编码需要解码,不能用常规的办法去生成,需要加上相应的参数(--base64-output=decode-rows -v)才能显示出sql语句; 新版本(5.7.7以后)binlog默认为ROW,且5.6新增了一个参数:binlog_row_image;把binlog_row_image设置为minimal以后,binlog记录的就只是影响的列,大大减少了日志内容 1.3. MIXED STATEMENT和ROW格式的混合,默认使用STATEMENT来记录binlog,当其无法实现主从复制的操作(例如函数操作)时,会切换到ROW格式来记录binlog。 2. 对比 接下来,我们使用mysql5.7.22版本来对比一下三种格式下的日志输出情况。 ...

2019-08-28 · 5 min · 893 words · Hank

Mysql开启Binlog并做数据备份

某些时候,一不小心删除了数据库的数据甚至某些表、库,顿时慌得一P,是不是想死的心都有了?尤其是在小公司或者小项目中,没有遵循严格的规章制度,每个开发人员都有测试数据库甚至生产库的权限,一些没有经验的程序员一顿胡乱操作,导致数据库的数据丢失的情况时有发生。此时,我们就需要用到Mysql的binlog了。 1. Binlog简介 Binlog,即binary log、二进制日志,记录了描述数据库更改的“事件”(EVENT),例如表创建操作或对表数据的更改(DELETE、UPDATE、INSERT),通俗的说,binlog主要用来记录对mysql数据更新或潜在发生更新的SQL语句(例如DELETE语句没有删除任何行也会记录),并以”事务”的形式保存在磁盘中Binlog;另外,它还包含有关每个语句获取更新数据的时间长度的信息。 二进制日志有两个重要目的: 数据同步:mysql master上的更改数据,通过binlog发送给slave节点,然后达到主从数据一致的目的 数据恢复:binlog为数据恢复提供了有效的手段,因为binlog记录了数据操作的内容、时间等关键信息,可以根据binlog来恢复数据 Binlog仅记录了DML和DDL,并不包括查询(SELECT和SHOW)语句。Binlog会稍微降低性能,但是对于其数据同步和数据恢复等优势而言,牺牲些许性能是值得的。 2. 开启binlog 可以在启动mysql是添加--log-bin[=base_name] 参数来启用binlog,base_name为日志文件的基础名称(base_name为空则默认使用pid-file选项的名称,该选项默认是主机名),通常建议自己设置base_name而不是使用默认的名称,或者通过修改mysql配置文件来开启。 我们看看如何修改mysql配置文件来开启binlog,环境使用的是ubuntu: 1、查看是否开启binlog 登录mysql,进入mysql命令行,输入如下命令查看: mysql> show variables like 'log_bin%' 结果如下: mysql> show variables like 'log_bin%'; +---------------------------------+--------------------------------+ | Variable_name | Value | +---------------------------------+--------------------------------+ | log_bin | ON | | log_bin_basename | /var/log/mysql/mysql-bin | | log_bin_index | /var/log/mysql/mysql-bin.index | | log_bin_trust_function_creators | OFF | | log_bin_use_v1_row_events | OFF | +---------------------------------+--------------------------------+ 5 rows in set (0.00 sec) 如果log_bin为OFF则未开启,ON则为已开启。另外有两个重要的信息: log_bin_basename:binlog存储的文件基础名称,完整的日志文件后缀会递增为形如0000x的格式,例如mysql-bin.00001 log_bin_index: binlog索引文件的名称,该文件存储了所有的binlog日志文件的位置 ...

2019-08-28 · 3 min · 631 words · Hank

Docker入门系列六——数据管理

容器的数据管理有两种方式: 数据卷(Data Volumes):容器内数据直接映射到本地主机环境 数据卷容器(Data Volume Containers):使用特定容器维护数据卷 1. 数据卷 数据卷是一个可供容器使用的特殊目录,将主机的目录映射进容器,类似于Linux中的mount行为。 特点: 可在容器间共享和重用,容器间传输数据变得高效 对数据卷内数据的修改会立即生效,容器和本地操作都可以 数据卷的更新不会影响镜像,应用和数据解耦 卷会一直存在,直到没有容器使用,就可以安全地卸载它 1.1. 数据卷管理 volume命令: root@ubuntu:~# docker volume --help Usage: docker volume COMMAND Manage volumes Commands: create Create a volume inspect Display detailed information on one or more volumes ls List volumes prune Remove all unused local volumes rm Remove one or more volumes 1.1.1. 创建数据卷 docker volume create -d local test 数据卷创建后存放在/var/lib/docker/volumes下。e.g. root@ubuntu:~# docker volume create -d local test test root@ubuntu:~# cd /var/lib/docker root@ubuntu:/var/lib/docker# ls builder buildkit containers image network overlay2 plugins runtimes swarm tmp trust volumes root@ubuntu:/var/lib/docker# cd volumes/ root@ubuntu:/var/lib/docker/volumes# ls metadata.db test root@ubuntu:/var/lib/docker/volumes# cd test/ root@ubuntu:/var/lib/docker/volumes/test# ls _data root@ubuntu:/var/lib/docker/volumes/test# ...

2019-08-20 · 4 min · 719 words · Hank

Docker入门系列五——仓库

docker 仓库的概念与java的maven仓库非常类似,它们都用来存储数据,maven仓库存储的是 jar 包,docker 仓库存储的是镜像。许多第三方私有仓库工具如 nexus 既可以构建 maven 仓库,也支持 docker 仓库。 1. 镜像仓库和注册表 1.1. Registry 镜像注册表,用来存储镜像数据的地方,官方的Docker hub就是一个公共的Registry,另外,还可以通过官方的registry镜像搭建私有的镜像注册表。通常所说的镜像仓库是泛指Registry,但并不完全准确,一个Registry可以包含多个Repository。 例如,拉取镜像:docker pull registry.hub.docker.com/ubuntu:18.04,这里的registry.hub.docker.com就是官方提供的镜像注册表,可以省略不写。 1.2. Repository 镜像库,包含多个镜像,存储于Registry中。在仓库搜索镜像时,按名称搜索在registry中查找repository。例如,我们所说的nginx镜像,一般就是指的nginx的Repository,它包含多个nginx镜像,它们通过tag来区分。 镜像的类别: 根镜像:Docker官方提供的基础镜像,单名字,如centos、ubuntu等 用户的镜像:由docker用户创建并维护的镜像,带有用户名前缀,表明是某用户下的仓库,例如:ansible/centos7-ansible,belonk/mynginx等 第三方镜像市场:阿里云、腾讯云、网易云等等 2. 搭建本地私有仓库 执行如下命令: docker run -d -p 5000:5000 registry:2 它会下载并启动一个registry容器,registry对应的是仓库镜像,版本为2。 上传镜像到本地私有仓库: 标记镜像: docker tag ubuntu IP:端口/镜像 上传镜像: docker push IP:端口/镜像 3. 基本操作 登录Docker hub Docker Hub是官方的公共镜像仓库,注册账号,然后在命令行进行登录: docker login 输入账号和密码登录即可,登录信息保存在 ~/.docker/config.json 下载镜像 docker [image] pull 搜索镜像 docker search IMAGE 上传镜像 docker [image] push [REGISTRY_HOST[:REGISTRY_PORT] / ]NAME[:TAG] ...

2019-08-12 · 1 min · 72 words · Hank

Docker入门系列四——容器

前一篇我们介绍了Docker的核心概念–镜像,知道了镜像是只读的创建容器的指令模板,由不同的层组成,也提到了镜像运行后就成为了容器,容器启动后回家镜像上增加一个可写的容器层,容器和镜像最主要的区别就在于容器层,容器层可读写,新写入或者修改的数据都存储在容器层上。在本篇,我们再来详细了解一下Docker的容器。 1. 简介 容器是镜像的可运行实例。您可以使用Docker API或CLI创建,启动,停止,移动或删除容器。您可以将容器连接到一个或多个网络,附加存储,甚至可以根据其当前状态创建新镜像。默认情况下,容器与其他容器及其主机相对隔离。您可以控制容器的网络,存储或其他基础子系统与其他容器或主机的隔离程度。容器由其镜像以及在创建或启动时为其提供的配置选项定义。删除容器后,其未持久存储的状态数据都将消失。 简单而言,容器可以看作是一个或一组独立运行的应用,以及这些应用必须的运行环境的一个整体。镜像是只读的,而容器则是在镜像上层添加了一个可写的容器层,镜像运行的实例就是容器。容器都有唯一的CONTAINER ID和名称(NAME),还包括该容器对应的镜像(IMAGE)、状态(STATUS)等属性,另外,容器可以被创建、启动、停止和删除。 例如,ubuntu是一个镜像,运行后,就得到了容器,多次运行则得到多个容器。 2. 容器操作 现在我们看看操作容器的一些基本命令。 2.1. 查看容器 查看容器列表 1、命令 docker container ls [OPTIONS] 改命令等同于: docker ps [OPTIONS] 选项: -a, --all:显示所有的容器(不加此选项则仅显示运行中的) -f, --filter:按照条件过滤查询 --format string: 格式化输出信息 -n, --last int: 仅显示最后的n个容器(所有状态) -l, --latest: 仅显示最近创建的容器(所有状态) --no-trunc: 不截断输出信息 -q, --quiet: 仅输出容器id -s, --size: 设置显示的总数 2、举例 查看本机运行的容器: root@ubuntu:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1dd6cb8df75d ubuntu "/bin/bash" 24 hours ago Up 24 hours dbdata2 d428c1240bc8 ubuntu "/bin/bash" 25 hours ago Up 25 hours db2 2442cb173404 ubuntu "/bin/bash" 25 hours ago Up 25 hours db1 db3ac83e2531 ubuntu "/bin/bash" 26 hours ago Up 25 hours dbdata ...

2019-07-29 · 7 min · 1411 words · Hank

Docker入门系列三——镜像

上一篇介绍了如何在Windows、macOS、Ubuntu上安装docker,安装完成后,我们将开始正式学习docker。Docker的三大核心概念分为是:镜像、容器和仓库,在本篇,我们将介绍第一个核心概念----镜像。 1. 简介 Docker镜像(Image),一个镜像是一个只读的用于创建docker容器(container)的指令模板,包括了程序运行所需的全部依赖包(包括程序、库、资源、配置等)。通常,镜像基于另一个镜像,并进行自定义。镜像是只读的、可运行的,运行后的镜像即为容器(Container)。 1.1. 镜像的几个概念 通过docker images查询本地的镜像列表时,可以看到镜像有REPOSITORY、ID、TAG等字段,镜像包含几个概念,首先需要弄清楚它们间的关系: Registry 镜像注册表,用来存储镜像数据的地方,官方的Docker hub就是一个公共的Registry,另外,还可以通过官方的registry镜像搭建私有的镜像注册表。通常所说的镜像仓库是泛指Registry,但并不完全准确,一个Registry可以包含多个Repository。 例如,拉取镜像:docker pull registry.hub.docker.com/ubuntu:18.04,这里的registry.hub.docker.com就是官方提供的镜像注册表,可以省略不写。 Repository 镜像库,包含多个镜像,存储于Registry中。在仓库搜索镜像时,按名称搜索在registry中查找repository。例如,我们所说的nginx镜像,一般就是指的nginx的Repository,它包含多个nginx镜像,它们通过tag来区分。 Tag 镜像的标签,一般用来作为版本区分,默认不写Tag为latest,一个Image有多个Tag。 Image 具体的镜像,有唯一的GUID和多个Tag。 简单而言,一个Registry包含多个Repository,每个Repository包含多个Image,每个Image有多个Tag,但是ID是唯一的。 1.2. 镜像层 镜像由不同的层(layer)组成。一般而言,Dockerfile每一个指令都会创建一个层,每层只是与之前图层的一组差异,它们都有自己的GUID,并且堆叠在彼此之上。当创建新容器时,将会在基础层的顶部添加新的可写层,称为“容器层”,对正在运行的容器所做的所有更改(例如,写入新文件、修改现有文件和删除文件)都将写入此可写容器层。 Figure 1. 镜像层示意 以镜像ubuntu15.04为例,运行一个容器,下图展示了其基本结构: Figure 2. 基于ubuntu15.04镜像的容器结构 上图展示了一个容器层(container layer)和镜像的多个镜像层(image layer)。其实,容器和镜像最主要的区别就在于容器层,容器层可读写,新写入或者修改的数据都存储在容器层上。基于同一镜像的不同容器都有自己的不同容器层,但是对于底层的各个镜像层,各个容器是共享的。当容器被删除后,只需删除该容器的容器层即可。可以看到,Docker通过层的设计极大地复用了资源,这也是docker轻量和快速的主要原因。 Figure 3. 基于ubuntu15.04镜像创建的多个容器结构 2. 镜像基本操作 接下来,我们看看镜像的一些基本操作。 2.1. 拉取镜像 拉取镜像意思是从镜像的Registry中将镜像下载到本地,类似于Git的pull拉取代码,命令如下: docker image pull [OPTIONS] NAME[:TAG|@DIGEST] 选项包括: -a, --all-tags: 拉取镜像的所有标签 --disable-content-trust: 是否跳过镜像验证,默认为true 例如:docker pull registry.hub.docker.com/ubuntu:18.04 2.2. 镜像查询 1、查询列表 查询本地的所有镜像: docker image ls 等同于docker images,例如: root@ubuntu:~# docker images REPOSITORY TAG IMAGE ID CREATED SIZE local/mynginx latest 4d24e58d851d 26 hours ago 108MB python 1.0 22a7b6b93718 5 days ago 131MB nginx latest f68d6e55e065 2 weeks ago 109MB python 2.7-slim ca96bab3e2aa 5 weeks ago 120MB hello-world latest fce289e99eb9 6 months ago 1.84kB ubuntu 14.10 a8a2ba3ce1a3 4 years ago 194MB ...

2019-07-23 · 6 min · 1111 words · Hank

Docker入门系列二——安装

在上一篇 Docker简介中,我们介绍了Docker的概念、优势以及Docker结构,也提到了Docker是基于Linux系统的cgroup技术实现的,既然如此,那么Docker是否支持非Linux系统上使用呢?答案是肯定的。Docker官方提供了Docker Machine工具,用于在非Linux系统上构建虚拟机并在其上安装Docker;Docker官方还提供了Docker Toolbox工具包,用来在旧版本的操作系统上安装Docker;另外,Docker还专门针对Windows和Mac OS,提供了更简单易用的Docker Desktop,用来一键安装Docker及其相关组件。\n 在本篇,我们将来看看如何在Linux、Mac OS和Windows上安装Docker。\n 本篇安装的docker版本在最新的18.09版本,后续的docker入门系列文章都是基于该版本。 1. Ubuntu上安装Docker 1.1. 系统要求 Docker要求安装与64位的Ubuntu,版本要求如下: Cosmic 18.10 Bionic 18.04 (LTS) Xenial 16.04 (LTS) Docker支持的架构包括:x86_64 (或amd64), armhf, arm64, s390x (IBM Z), and ppc64le (IBM Power)。 测试Linux服务器为Ubuntu,版本为16.04,Ubuntu发布代码为xenial,其他版本的Linux就不做实践了: root@ubuntu:~# cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS" 1.2. 准备工作 1、卸载旧版本 如果原来安装过旧版本的docker,需要先卸载之: $ sudo apt-get remove docker docker-engine docker.io containerd runc 旧版本的docker在Ubuntu的镜像仓库中名为docker.io,docker-io包是Debian/Ubuntu用于官方docker发布的名称,不叫docker的原因在于,避免与Ubuntu的docker system-tray二进制文件发生名称冲突。在新版本的docker中,docker已经区分为docker-ce和docker-ee版本,在安装新版本的docker时,需要先卸载旧版本docker。 2、添加仓库地址 安装前,需要先将docker官方安装地址加入到Ubuntu apt地址库中。 (1)更新package索引 apt-get update ...

2019-07-12 · 2 min · 412 words · Hank

Docker入门系列一——简介

1. 简介 容器化技术,是对Linux容器的一种封装,将程序打包为镜像文件,运行该文件创建容器,最终达到使用容器进行快速、简单、高效的开发、部署、运行软件程序的目的。相对于虚拟化技术 [1],容器化技术有着更高的资源利用率、更高的性能、更低的开发和维护成本。 容器化技术有以下特点: 灵活性:绝大部分应用程序都可以使用容器化技术进行开发和运维,并且可及时部署和更新应用 轻量级:共享宿主机内核资源,仅将程序和其依赖打包到镜像文件,运行于容器,占用资源少、性能高 可移植:一次构建,到处运行;本地一次构建,然后可部署到各个平台或云端docker环境中,强移植性 可扩展:可以自定义镜像文件,扩展功能满足不同需要 可堆叠:您可以垂直和即时堆叠服务 Docker就是一种容器化技术。 Docker是一个开发、运输和运行应用程序的开放平台,基于Linux内核的 cgroup [2] 技术实现,根据开源Apache 2.0许可证授权。使用Docker,您可以将应用程序与基础架构分离,以便快速交付软件,并且像管理应用程序一样管理基础架构。通过Docker快速发布、测试和部署代码,可以显着减少编码和部署程序的工作量,大大提高工作效率。 Figure 1. Docker场景示意图 1.1. 容器和虚拟机 容器运行在宿主机并且和其他容器共享宿主机内核资源,运行于独立的进程,不消耗更多内存及CPU资源,这使得容器很轻量级。 虚拟机也是运行在宿主机上,但是虚拟机有自己完全独立的操作系统,通过虚拟管理程序与宿主机交换资源,各个虚拟机共用宿主机的资源很少,导致虚拟机占用资源高、资源利用率低。 Figure 2. docker容器化技术结构 Figure 3. 虚拟化技术结构 如图2所示,Docker下层是其基础服务层,上层是docker容器,多个容器之间相互隔离,docker共享宿主机(Host OS)的系统资源。 而虚拟化技术结构如图3所示,底层是hypervisor [3] 层,用来协调多个虚拟机(VM)和宿主机(物理机)的资源共享,上层的多个虚拟机都有单独的操作系统,他们之间不进行资源共享。 不难看出,虚拟机提供给运行其上的程序过量的资源,资源利用不充分,性能低;而容器技术,很好的解决了这一问题。 1.2. Docker可以做什么 1、快速,一致地交付您的应用程序 Docker允许开发人员使用提供应用程序和服务的本地容器在标准化环境中工作,从而简化了开发生命周期。容器非常适合持续集成和持续交付(CI / CD)工作流程。 2、响应式部署和扩展 Docker基于容器的平台允许高度可移植的工作负载。Docker容器可以在开发人员的本地笔记本电脑,数据中心的物理或虚拟机,云提供商或混合环境中运行。 Docker的可移植性和轻量级特性还使得可以轻松地动态管理工作负载,按照业务需求即时扩展或拆除应用程序和服务。 3、在同一硬件上运行更多工作负载 Docker轻巧而快速。它为基于管理程序的虚拟机提供了一种可行且经济高效的替代方案,因此您可以使用更多的计算容量来实现业务目标。Docker非常适合高密度环境以及需要用更少资源完成更多工作的中小型应用部署,譬如现非常流行的微服务架构。 2. Docker核心概念 Docker有三大核心概念,分别是镜像、容器和仓库。 Docker镜像 Docker镜像(Image),一个镜像是一个只读的用于创建docker容器(container)的指令模板。通常,镜像基于另一个镜像,并进行自定义。一般而言,通过定义Dockerfile文件来创建镜像。 Docker容器 Docker容器(Container),容器是镜像的运行实例,Docker的容器间是相互隔离的,可以将容器看做是简化版的Linux系统和运行在其中的应用打包而成的沙箱。您可以使用Docker API或CLI创建、启动、停止、移动或删除容器,也可以将容器连接到一个或多个网络,附加数据卷,甚至可以根据其当前状态创建新镜像。 Docker仓库 Docker仓库指的是镜像文件存储的地方。Docker还有一个镜像注册表(Registry)的概念,它是存放镜像仓库的地方,有许多的仓库存放于其中。仓库有公共仓库和私有仓库之分,最大的公共仓库是Docker hub,用户也可以搭建自有的私有镜像仓库。用户创建了镜像过后,通过push命令将其推送到镜像仓库中,使用时再从镜像仓库pull到本地运行,这设计类似于Git的代码仓库。 3. Docker结构 Figure 4. docker的整体结构 Docker整体结构如图4所示。一般而言,我们所说的docker,均指docker引擎(Docker Engine)。 3.1. Docker引擎 Docker Engine是一个客户端 - 服务器架构的应用程序,我们常说的docker指的就是docker engine,它包含以下主要组件: 服务器,是一种长时间运行的程序,称为守护进程( dockerd命令) REST API,程序与守护进程通信的接口 CLI,命令行界面,即docker客户端(docker命令),负责接收用户指令并通过REST API请求守护程序,完成与守护进程交互 ...

2019-07-03 · 1 min · 203 words · Hank

使用Github的Webhook实现代码库与应用的交互

很早之前,博主的的网站评论模块已经更改为了 gitalk,Gitalk是一个基于GitHub Issue和Preact的现代评论组件。更换评论组件后有一个问题:我不想每次去github上查看别人的评论,如果别人评论了文章,然后我能够在网站后台看到,这样就很方便了。此时,github的webhook功能就可以登场了。 1. 简介 Webhook,翻译过来可以称为网络钩子,用来将Github上的一系列事件信息回传到某一回调地址上,从而完成与外部应用的交互,它是github提供的一种与外部交互的入口。Github上提供了很多交互事件,当某一事件被触发后,如果设置了Webhook的回调地址,Github将会通过HTTP POST请求将事件信息发送到回调地址上,回调处理应用通过接收事件信息然后实现自身的业务需求。 目前,每个组织或代码库上最多只能创建20个webhook。 一个典型的业务场景是:代码保存在github上,如果稳定的master分支上提交了代码,就触发持续集成系统如Jenkins进行代码构建、打包、部署等系列操作。 2. 事件 配置webhook时,可以选择订阅的事件。一般情况下,我们只需要订阅关注的事件,github也支持一个匹配所有支持事件的通配符(*),添加通配符事件时,github将使用通配符事件替换您配置的任何现有事件,并为所有支持的事件发送有效负载。如果将来添加了新的可匹配的事件,那么将会自动订阅。您可以随时通过API或UI更改订阅事件列表。默认情况下,webhooks仅订阅push事件。 每个事件对应于您的组织和/或存储库可能发生的某组操作。例如,如果您订阅了问题事件,则每次打开,关闭,标记等issue时您都会收到详细的有效负载。github现支持的事件列表见 #events[附录一]。 2.1. 有效负载 有效负载,简单理解就是消息所携带的内容和信息。 每种事件类型都具有特定的有效负载格式以及相关的事件信息,事件的有效负载来源于具体的事件类型的有效负载,但原始push事件除外,它的具有更详细的webhook有效负载。 除了为每个事件记录的字段之外,webhook有效负载还包括执行事件的用户(发送者)、组织和事件发生的存储库。GitHub app的webhook有效负载还可能包括安装事件。 一个marketplace_purchase事件类型的有效负载的示例如下: { "action":"purchased", "effective_date":"2017-10-25T00:00:00+00:00", "sender":{ "login":"username", "id":3877742, "avatar_url":"https://avatars2.githubusercontent.com/u/3877742?v=4", "gravatar_id":"", "url":"https://api.github.com/users/username", "html_url":"https://github.com/username", "followers_url":"https://api.github.com/users/username/followers", "following_url":"https://api.github.com/users/username/following{/other_user}", "gists_url":"https://api.github.com/users/username/gists{/gist_id}", "starred_url":"https://api.github.com/users/username/starred{/owner}{/repo}", "subscriptions_url":"https://api.github.com/users/username/subscriptions", "organizations_url":"https://api.github.com/users/username/orgs", "repos_url":"https://api.github.com/users/username/repos", "events_url":"https://api.github.com/users/username/events{/privacy}", "received_events_url":"https://api.github.com/users/username/received_events", "type":"User", "site_admin":true, "email":"username@email.com" }, "marketplace_purchase":{ "account":{ "type":"Organization", "id":18404719, "login":"username", "organization_billing_email":"username@email.com" }, "billing_cycle":"monthly", "unit_count":1, "on_free_trial":false, "free_trial_ends_on":null, "next_billing_date":"2017-11-05T00:00:00+00:00", "plan":{ "id":435, "name":"Basic Plan", "description":"Basic Plan", "monthly_price_in_cents":1000, "yearly_price_in_cents":10000, "price_model":"per-unit", "has_free_trial":true, "unit_name":"seat", "bullets":[ "Is Basic", "Because Basic " ] } } } ...

2019-07-02 · 2 min · 408 words · Hank

Spring AMQP消息转换

上一篇,我们介绍了如果使用Spring AMQP注解来实现消息发送和监听,示例都是使用的默认的消息转换器,即SimpleMessageConverter,它只能处理byte[]、String、java序列化对象(实现了Serializable接口的对象)。 通常,不推荐使用Java序列化,因为它存在与Java对象强耦合、依赖java语言等缺点,Spring AMQP也提供了其他的消息转换方式,在本篇,我们将重点来看看如果将消息序列化为JSON格式。 1. MessageConverter Spring AMQP消息转换定义了顶层接口MessageConverter,它的定义如下: public interface MessageConverter { // 将对象转换为Message对象,支持自定义消息属性 Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException; // 将Message转换为对象 Object fromMessage(Message message) throws MessageConversionException; } 它定义了两个方法:将对象转换为Message,将Message转换为对象。 同时,在AmqpTemplate中定义了便捷的消息转换和发送的方法: void convertAndSend(Object message) throws AmqpException; void convertAndSend(String routingKey, Object message) throws AmqpException; void convertAndSend(String exchange, String routingKey, Object message) throws AmqpException; void convertAndSend(Object message, MessagePostProcessor messagePostProcessor) throws AmqpException; void convertAndSend(String routingKey, Object message, MessagePostProcessor messagePostProcessor) throws AmqpException; void convertAndSend(String exchange, String routingKey, Object message, MessagePostProcessor messagePostProcessor) throws AmqpException; ...

2019-06-17 · 3 min · 488 words · Hank

Spring AMQP注解的使用

上一篇 "Spring AMQP简介和使用",我们介绍了Spring AMQP的一些基本要素和概念,也通过一些示例代码介绍了消息的发送和接收,但是都是使用的原始编码方式来实现,并不依赖Spring环境。其实,Spring AMQP也支持使用注解的方式来进行异步接收消息,极大的简化了编码。 1. hello world 要使用注解,首先需要在Spring应用环境中,我们看一个最简单的demo: 1、重新新建一个Spring boot工程,添加如下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-amqp</artifactId> <version>1.7.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>1.7.6.RELEASE</version> </dependency> 2、新建一个Spring配置类RabbitConfiguration,用来申明Bean: @Configuration public class RabbitConfiguration { public static final String ANONYMOUS_QUEUE_NAME = "spring.amqp.anonymous.queue"; @Bean public ConnectionFactory connectionFactory() { CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory("192.168.0.27", 5672); cachingConnectionFactory.setUsername("admin"); cachingConnectionFactory.setPassword("123456"); return cachingConnectionFactory; } @Bean public AmqpAdmin amqpAdmin() { return new RabbitAdmin(connectionFactory()); } @Bean public RabbitTemplate rabbitTemplate() { return new RabbitTemplate(connectionFactory()); } @Bean public Queue anonymousQueue() { // 匿名队列 return new AnonymousQueue(() -> ANONYMOUS_QUEUE_NAME); } } ...

2019-06-13 · 5 min · 990 words · Hank

Spring AMQP简介和使用

1. 前言 很早之前写过几篇关于RabbitMQ的一些基础文章,在本篇中,我们将来学习Spring AMQP。 Spring AMQP 是 Spring 对 AMQP( http://www.amqp.org) 协议的封装和扩展,它提供了模板来对消息的发送和接收进行高级抽象,还提供了基于消息驱动的POJO的支持(类似JMS,Java消息服务)。 在开始之前,首先需要了解一些 RabbitMQ 和 AMQP 的一些基础概念,并在你的机器上安装 RabbitMQ, 本文使用的spring-amqp的版本为1.7.6。 2. Spring AMQP抽象 Spring AMQP 是 Spring 对 AMQP 协议的封装和扩展,提供了消息发送和接收的模板。Spring AMQP项目将核心Spring概念应用于基于 AMQP 的消息传递解决方案的开发,以便更容易和简单的管理AMQO资源。 Spring AMQP由spring-amqp和spring-rabbit两个模块组成。spring-amqp模块位于org.springframework.amqp.core包,它的目标是提供不依赖于任何特定AMQP代理实现或客户端库的通用抽象;而spring-rabbit是spring-amqp通用抽象的具体实现,目前仅提供了rabbitmq的实现。 Spring AMQP包括一些基本的抽象定义(上边说过,他们位于org.springframework.amqp.coreb包中,而非AMQP协议本身定义): 2.1. Message 在0-9-1版本的 AMQP 规范中没有定义 Message 类或接口,当执行诸如 basicPublish() 操作时,内容作为字节数组参数传递,而其他属性作为单独的参数传递。Spring AMQP将 Message 类定义为更通用的AMQP域模型表示的一部分。Message该类的目的是将主体和属性封装在单个实例中,以便API可以更简单。以下示例显示了Message类定义: public class Message { private final MessageProperties messageProperties; private final byte[] body; public Message(byte[] body, MessageProperties messageProperties) { this.body = body; this.messageProperties = messageProperties; } public byte[] getBody() { return this.body; } public MessageProperties getMessageProperties() { return this.messageProperties; } } ...

2019-06-05 · 8 min · 1703 words · Hank

Spring boot集成quartz并实现任务调度

最近在实现任务调度服务,使用 quartz调度框架,由于使用的spring boot版本为1.5.10,该版本并没有提供quartz的starter,在集成的时候还是遇到了很多问题。本文并不会详细介绍quartz框架,请自行查阅相关资料。如果对Spring boot不太熟悉,可以看前边几篇关于spring boot的文章。 1. quartz简介 1.1. 核心概念 scheduler:任务调度器,相当于指挥中心,负责管理和调度job,由SchedulerFactory创建和关闭,创建后需要通过starter()方法来启动调度。 trigger:触发器,定义job的触发时机,通过TriggerBuilder来创建,有SimpleTrigger和CronTrigger,前者可以定义在某一个时刻触发或者周期性触发,后者使用cron表达式来定义触发时间。 Job:具体的调度业务逻辑实现,也就是任务,具体任务需要实现org.quartz.Job接口。 JobDetail:org.quartz.JobDetail用来描述Job实例的属性,并且可以通过JobDataMap传递给Job实例数据,通过JobBuilder来创建。 1.2. quartz配置 quartz框架默认会通过quartz.properties文件来加载配置信息,有几个重要的配置项: org.quartz.scheduler.*:调度器相关配置 org.quartz.threadPool.*:线程池相关配置 org.quartz.jobStore.*:quartz数据存储相关配置,在quartz中,有三种存储类型: RAMJobStore:基于内存存储job JobStoreCMT:基于数据库的数据存储,并且受运行的java容器的事务控制 JobStoreTX:基于数据库的数据存储,不受事务控制 org.quartz.dataSource.*:配置数据库存储quartz数据时的数据源信息 org.quartz.plugin.*:quartz插件相关配置 quartz简单介绍这么多,更多信息请看 这里。 2. Spring boot与quartz集成 前边说过,我使用的spring boot版本为1.5.10,没有quartz的starter,所以需要自己编码实现。同时,我还需要对quartz做持久化处理,需要在mysql库中导入quartz的建表脚本。集成步骤如下: 1、引入依赖 引入quartz的依赖包,quartz-jobs根据实际需要决定是否引入。 <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.3</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.2.3</version> </dependency> 2、创建表 在下载的quartz包中有建表脚本,我的为quartz-2.2.3,建表脚本在/docs/dbTables下边,导入tables_mysql_innodb.sql即可。 3、配置文件 在resources目下新建一个quartz.properties配置文件,我的配置如下: #============================================================================ # Configure Main Scheduler Properties #============================================================================ org.quartz.scheduler.instanceName:event-scheduler org.quartz.scheduler.instanceId:AUTO org.quartz.scheduler.skipUpdateCheck:true #============================================================================ # Configure ThreadPool #============================================================================ org.quartz.threadPool.class:org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount:5 org.quartz.threadPool.threadPriority:5 #============================================================================ # Configure JobStore #============================================================================ org.quartz.jobStore.misfireThreshold:60000 org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.useProperties:false org.quartz.jobStore.tablePrefix:QRTZ_ org.quartz.jobStore.isClustered:false #============================================================================ # Configure Datasources #============================================================================ org.quartz.dataSource.ds.maxConnections:7 #============================================================================ # Configure Plugins #============================================================================ org.quartz.plugin.triggHistory.class: org.quartz.plugins.history.LoggingTriggerHistoryPlugin ...

2019-01-22 · 4 min · 647 words · Hank

Spring boot工程Feign请求时Get请求无法传递body的问题

前一篇,我们解决了Spring的RestTemplate在发送Get请求不能传递body,相比RestTemplate,我们更多的还是采用更方便的申明式服务调用框架Feign。本篇不会介绍Feign,而是解决Feign发送Get请求时仍然不能传递body的问题。 1. 准备 在这里,我使用的Spring boot的版本为1.5.10.RELEASE,Spring Cloud版本为Edgware.SR3。 准备三个服务:注册中心、服务生产者、服务消费,这里我们继续使用在 Spring Cloud服务注册中心Eureka定义的三个服务:服务注册中心01-eureka-server、服务提供者01-service-demo,同时新建一个服务消费者01-feign-consumer,使用feign来请求服务而不是ribbon,记得在启动类加上@EnableFeignClients启用Feign。 1、在服务提供者01-service-demo添加一个接口,代码如下: @GetMapping("/hello/user") public String sayHello(@RequestBody User user) { return "hello, " + user.getName() + ", give you a gift " + user.getGift(); } 这样,Get请求需要接收body数据。 2、在服务消费者01-feign-consumer编写如下代码: (1)在SayHelloClient中添加如下声明式服务请求: @FeignClient("hello-service") public interface SayHelloClient { @GetMapping(value = "/hello/user") String sayHello(@RequestBody User user); } (2)在SayHelloController中调用SayHelloClient: @RestController public class SayHelloController { private static Logger log = LoggerFactory.getLogger(SayHelloController.class); @Autowired private ApiProductClient apiProductClient; @GetMapping("/user") public String userHello() { User user = new User(); user.setName("lily"); user.setGift("birthday card"); return sayHelloClient.sayHello(user); } } ...

2019-01-22 · 1 min · 125 words · Hank

使用Spring的RestTemplate发送GET请求,并支持传递Request body参数

最近在使用Spring Boot实现微服务,都是使用RESTful风格的Api接口,服务间使用RestTemplate来进行HTTP通信,遇到这样一个需求:开发一个查询请求Api,参数使用JSON格式的字符串来提交。 1. 请求格式 希望的请求格式如下: GET /pointCard/ HTTP/1.1 Host: localhost:8100 Content-Type: application/json;charset=UTF-8 Content-Length: 114 {"iColumns":7,"iDisplayLength":10,"iDisplayStart":0,"iSortingCols":0,"sColumns":"","sEcho":1,"subjectId":"11227"} 在RESTful下,这样的设计是合理的,GET请求表示从服务器获取资源,但需要将查询参数以JSON格式来提交。但是,这违背了传统的GET请求的规范,我们都知道,GET请求只能将请求参数拼接URI后边,而不能单独传递request body参数,除非你改用POST。 2. 代码实现 我们先来编一个上述请求的API,然后进行测试。 1、编写一个API: @GetMapping(value = "/") public Response getById(@RequestBody @Valid PointCardQuery query) throws Exception { Assert.notNull(query,"查询条件不能为空!"); …… return Response.success(pointCardPurePager, "积分卡获取成功!"); } 上边的代码片段处于一个Restcontroller,要求使用GET方法,并且使用了@RequestBody注解来获取request body参数。 2、我们使用RestTemplate来测试一下: @Test public void testGetWithBody() { RestTemplate restTemplate = new RestTemplate(); String p = "{\"iColumns\":7,\"iDisplayLength\":10,\"iDisplayStart\":0,\"iSortingCols\":0,\"sColumns\":\"\",\"sEcho\":1,\"subjectId\":\"11227\"}"; String url = "http://localhost:8100/pointCard/"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); HttpEntity<String> httpEntity = new HttpEntity<>(p, headers); ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class); String body = responseEntity.getBody(); System.out.println(body); System.out.println(responseEntity.getStatusCode()); System.out.println(responseEntity.getStatusCodeValue()); System.out.println(responseEntity); } ...

2018-10-31 · 2 min · 341 words · Hank

Spring Boot参数验证(下)——Bean Validation在Web中的应用

在 Spring Boot参数验证(上)--Bean Validation及其Hibernate实现 一篇中,我们介绍了验证标准Bean Validation和其Hibernate实现,在本篇,我们看看它们是如何应用在Spring Boot Web项目中。 1. Spring Validator 其实,Spring很早就有了自己的Bean验证机制,其核心为Validator接口,表示校验器: public interface Validator { // 检测Validator是否支持校验提供的Class boolean supports(Class<?> clazz); // 校验逻辑,校验的结果信息通过errors获取 void validate(@Nullable Object target, Errors errors); } Errors接口,用以表示校验失败的错误信息: public interface Errors { // 获取被校验的根对象 String getObjectName(); // 校验结果是否有错 boolean hasErrors(); // 获取校验错误数量 int getErrorCount(); // 获取所有错误信息,包括全局错误和字段错误 List<ObjectError> getAllErrors(); // 获取所有字段错误 List<FieldError> getFieldErrors(); …… } 当Bean Validation被标准化过后,从Spring3.X开始,已经完全支持JSR 303(1.0)规范,通过Spring的LocalValidatorFactoryBean实现,它对Spring的Validator接口和javax.validation.Validator接口进行了适配。 1.1. 全局Validator 全局Validator通过上述的LocalValidatorFactoryBean类来提供,只要使用@EnableWebMvc即可(Xml配置开启<mvc:annotation-driven>),也可以进行自定义: @Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { @Override public Validator getValidator(); { // return "global" validator } } ...

2018-10-12 · 3 min · 624 words · Hank

Spring boot全局异常处理和自定义异常页面

Spring boot提供了默认的异常处理机制,但是难以满足业务需要,一般需要编码来实现自己的业务处理机制。在本篇,将介绍如何自定义异常页面,和进行全局的异常处理。 1. 全局异常处理 如果系统业务处理发生异常,我们希望能够将异常信息以友好的形式返回给调用方,而不是仅在后台记录日志,尤其是在开发RESTFul的API时,需要将业务异常信息进行明确定义并返回给API调用方,这显得尤为重要。 现在,我们来定义一个全局的业务异常类BusinessException,如果业务处理失败抛出该类或者其子类,然后编写一个全局异常处理器,将异常转换为有好的信息并返回。 1、定义异常类 public class BusinessException extends Exception { public BusinessException() { super(); } public BusinessException(String msg) { super(msg); } public BusinessException(Throwable cause) { super(cause); } public BusinessException(String message, Throwable cause) { super(message, cause); } public BusinessException(MsgDefinition msgDefinition) { super(msgDefinition.msgOf()); this.msgDef = msgDefinition; } public MsgDefinition msgDef() { return msgDef == null ? MsgDefinition.UNKOWN_ERROR : msgDef; } } 其中的MsgDefinition为具体的错误信息,包含错误码code和错误提示信息msg,子类继承该类进行扩展: public class MsgDefinition { public static final MsgDefinition SUCCESS = new MsgDefinition("0000", "请求成功"); public static final MsgDefinition EMPTY_ARGUMENTS = new MsgDefinition("4001", "请求参数为空"); public static final MsgDefinition ILLEGAL_ARGUMENTS = new MsgDefinition("4002", "请求采参数非法"); public static final MsgDefinition FILE_SIZE_OVER_LIMIT = new MsgDefinition("4301", "文件大小超过限制"); public static final MsgDefinition FILE_NUMBER_OVER_LIMIT = new MsgDefinition("4302", "文件数量超过限制"); public static final MsgDefinition FILE_FORMAT_UNSUPPORTED = new MsgDefinition("4310", "文件格式不支持"); public static final MsgDefinition UNKOWN_ERROR = new MsgDefinition("9999", "系统未知异常"); private String code; private String msg; public MsgDefinition(String code, String msg) { this.code = code; this.msg = msg; } public String codeOf() { return this.code; } public String msgOf() { return this.msg; } @Override public String toString() { return JsonUtil.toJson(this); } } ...

2018-10-10 · 2 min · 383 words · Hank

Spring Boot参数验证(上)——Bean Validation及其Hibernate实现

通常,web项目都需要对请求的参数进行校验,一般是前端JavaScript需要校验,然后后台还需要再进行校验,而校验逻辑大多数是重复的。如果在后台通过硬编码的方式,对Controller参数进行逐个校验,将会非常耗时和低效的,尤其是在使用Spring Boot和Spring Cloud的微服务体系中,大多数服务均会对外提供RESTFul的api,如果有统一的验证机制和框架,将大大提高生产力。幸运的是,Java已经提供了标准的验证API,称为Bean Validation。 1. 简介 1.1. Bean Validation Bean Validation,是JCP(Java Community Process)定义的标准化的JavaBean校验API,基于注解,并且具有良好的易用性和扩展性,1.0版本定义为 /attachment/20181009/28f52319fd84452ba22b490ff911fd04.pdf[JSR 303],而现在发布了2.0版本,定义为 JSR 380,了解详细信息可以看 这里。 Bean Validation并不局限于应用程序的某一层或者哪种编程模型, 它可以被用在任何一层, 也可以是像Swing这样的富客户端程序中. 目标:简化Bean校验,将以往重复的校验逻辑进行抽象和标准化,形成统一API规范; 版本变化:JSR 303在2009发布了1.0Final版,而最新的是在2017年发布的Bean Validation 2.0,被定义为 JSR 380。 需要注意的是,Bean Validation只是一个规范和标准,并没有提供实现,而接下来介绍的hibernate validator就是它的一种实现。 1.2. Hibernate Validator 是JSR 380的一种标准实现,同时还对其进行了扩展,如增加了部分验证约束。目前,最新的稳定版本为 6.0.13.Final。 2. 入门示例 接下来,我们使用Spring Boot来编写一个简单的示例工程。如果对Spring Boot不熟悉的可以看这几篇文章: "Spring Boot之基础入门" "SpringBoot-工程结构、配置文件以及打包" "Spring Boot JPA使用详解" 1、添加maven依赖 前边说过,JSR 303定义了JavaBean的验证标准,而Hibernate Validator是它的一种实现,所以,这两个的jar包我们都需要添加到工程。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> 其实,spring-boot-starter-web启动器已经依赖了hibernate-validator,而后者很明显会依赖validation-api。 ...

2018-10-10 · 8 min · 1686 words · Hank

Spring Cloud服务注册中心Eureka高可用

在 上一篇,我们通过编码,分别编写了服务注册中心、服务提供者、服务消费者,但是服务注册中心仍然是单节点,如果服务注册中心不可用,则会导致整个体系的所有服务都不可用。所以,有必要保证服务注册中心的高可用,本章将来讨论整个问题。 1. Eureka集群 对于Eureka而言,其本身是支持多节点集群的。其原理大致如下:服务注册中心的多个实例彼此互相注册,形成注册中心的集群,同时对外提供服务,即使其中一个挂掉,其他实例仍然可以对外提供服务,从而保证了高可用。 Figure 1. Eureka双节点结构示意图 以双节点为例,如上图所示,两个服务注册中心(Eureka Server)彼此相互注册形成集群,服务提供者(Service Provider)会自动向两者都进行服务注册,即是说,两个服务注册中心有用相同的服务实例列表,服务消费者(Service Consumer)会采用负载均衡算法从服务注册中心集群中选择出一个可用的服务注册中心,来获取服务注册实例,并完成请求。 上一篇已经说过,默认情况下,每一个Eureka服务端同样也是一个Eureka客户端,因此至少需要配置一个其他的Eureka服务端URL来让自己完成注册。如果不提供这个服务端URL,服务端本身可以正常运行和提供服务,但是控制台会打印许多无法成功注册的日志信息。在上一篇在编码单节点服务注册中心时,我们做了如下配置,让其不作为客户端让其他服务注册中心注册: eureka.client.register-with-eureka=false eureka.client.fetch-registry=false 这两项配置默认均为true,即是说eureka默认是开启集群功能的。 2. 编码实战 接下来,我们参照上一篇的服务注册中心、服务提供者、服务消费者代码,来组建一个双节点的服务注册中心,并通过服务消费者获取服务实例,完成远程请求。 2.1. 服务注册中心 1、新建一个名为02-eureka-server-nodes的Spring Boot工程,作为服务注册中心 2、引入如下依赖和插件: <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> 引入spring-boot-maven-plugin的目的是为了打可执行jar包,由于要组建双节点集群,需要启动两次,所以不能直接在idea里边运行工程了,需要打包成可执行jar,通过命令行来启动。 3、启动类添加@EnableEurekaServer注解,表明该工程为Eureka服务端 4、配置多环境,新建三个配置文件:application.properties、application-node1.properties、application-node2.properties,分别进行如下配置: application.properties: spring.application.name=eureka-server # 开发用,关闭服务端自我保护,防止关闭的实例不能被剔除 eureka.server.enable-self-preservation=false application-node1.properties: server.port=8080 eureka.instance.hostname=eureka-node1 # 服务注册中心访问地址 eureka.client.service-url.defaultZone=http://eureka-node2:8081/eureka application-node2.properties: server.port=8081 eureka.instance.hostname=eureka-node2 # 服务注册中心访问地址 eureka.client.service-url.defaultZone=http://eureka-node1:8080/eureka 需要注意的是,eureka-node1需要向eureka-node2来注册服务,而eureka-node2需要向eureka-node1来注册服务,彼此交叉注册,注意配置地址的不同。 5、由于在本机测试,并且这里通过hostname来访问服务,所以需要让hostname可以访问,修改hosts文件进行本机映射: 127.0.0.1 eureka-node1 127.0.0.1 eureka-node2 各操作系统修改hosts文件的方式不同,请自行查阅相关资料。 6、到这里编码工作就已完成,现在,使用maven命令进行打包: ``mvn clean package`` ...

2018-08-16 · 1 min · 202 words · Hank

Spring Cloud服务注册中心Eureka

上一篇,我们学习了Spring Cloud配置集成化组件Config,在本篇,我们来学习Spring Cloud微服务架构的一个核心组件----Eureka。 1. 服务治理 服务治理,即将体系中各个微服务统一纳入服务注册中心,由注册中心维护各个微服务的名称、地址、端口、状态等信息,并提供服务自动化注册和发现机制。 为什么需要服务治理? 对于传统应用而言,应用数量少,各应用间的API调用我们可以通过在配置文件中写入IP地址和端口的方式来进行维护。那么对于现在的微服务架构而言,应用的服务数量上百甚至上千,如果还使用传统的配置方式来维护各个服务信息及其其调用管理,那么无疑是困难重重,不仅容易出错,而且成本直线上升。 <p style="text-align: center;">没有服务治理时,如何管理各个服务? <p style="text-align: left;">在微服务体系中,服务实例的网络位置都是动态分配的,而且实例的地址可能会经常性的改变,有了服务治理,不再需要人为的维护各个服务信息,一切交给服务注册中心来自动管理和维护,大大提高了效率。 几个核心概念 服务注册表:维护了服务实例的网络地址、名称等信息的数据库 服务提供者:提供RESTApi以供其他服务调用的被调用方 服务消费者:调用服务端提供的RESTApi接口,发起服务请求的请求方 服务注册:将服务注册表中提供服务信息进行注册服务的行为 服务发现:从服务注册表中查询服务实例信息的行为 接下来,我们看看服务发现的两种模式。 服务发现模式 服务发现主要有两种模式:服务端发现模式和客户端发现模式。 1、客户端发现模式 即由客户端来获取服务实例列表和网络位置,并且进行负载均衡。客户端查询服务注册表(一个维护了服务实例和其网络位置等信息的数据库),获取服务实例列表,并且采用负载均衡算法,从列表中选择一个服务实例作为请求目标。 Figure 1. 客户端发现模式示意图 Eureka采用的就是客户端发现模式。 2、服务端发现模式 即由服务端来发现服务实例并进行负载均衡:客户端通过负载均衡器向某个服务提出请求,负载均衡器查询服务注册表,并将请求转发到可用的服务实例。 Figure 2. 服务端发现模式示意图 AWS Elastic Load Balancer(ELB)采用的是服务端发现模式。 本篇讲解的Spring Cloud Eureka采用的是客户端发现模式,接下来我们看看eureka的基本概念。 2. Eureka简介 Eureka,字面意思即"发现"之意,是Netflix下开源的服务治理(注册、发现等)中间件。Spring Cloud Eureka在其基础上进行了二次封装,添加了自动化配置等功能,使其成为Spring Cloud微服务体系中核心的服务治理方案之一。 Eureka包括三个角色,如下图所示: Figure 3. Eureka基础架构(图片来源网络) Eureka Server:服务注册中心,维护了服务注册表,提供服务注册、服务发现、刷新和取消注册等功能 Service Provider:服务提供者,对外提供服务,一般为RESTApi接口 Service Consumer:服务消费者,调用服务提供者接口,实现具体业务逻辑 由上图可见,Service Provider将注册中心注册服务,Service Consumer从注册中心查询服务注册表,获取服务实例信息,并通过负载均衡算法获取到一个服务提供者实例,并发起调用请求。 Eureka Server并没有后端存储机制,注册表中的所有服务实例必须发送心跳监测以保证正确连接(内存中实现),服务实例(客户端)会将服务端注册表拉去到本地并缓存在内存中,而不是每次都向服务端发送请求来获取服务实例注册信息。 前边介绍了Eureka和服务治理相关的理论,接下来,我们来编程实现服务注册中心。 3. 单节点服务注册中心 3.1. 编写服务注册中心 1、新建名为01-eureka-server的Spring Boot工程,作为服务注册中心 2、引入如下依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> ...

2018-08-15 · 2 min · 247 words · Hank

Spring Cloud Config之配置集成化

在 Spring Cloud简介中,我们了解了Spring Cloud的基本概念、特点、各大组件和其作用,还介绍了Spring Cloud的版本定义和发布规则,对Spring Cloud应该有了一定的了解。在本篇,我们来看看实现配置集成化的重要组件----Spring Cloud Config。 1. 简介 Spring Cloud Config是一款配置集中管理的组件,它提供了分布式系统的客户端和服务端外部化配置支持,由git(也支持svn、本地文件系统等)存储库支持的集中式外部配置管理,实现了配置的外部化存储。 Spring Cloud Config有几个核心的概念: 配置仓库:集中存储配置文件的地方, 推荐git仓库,也可以使用svn仓库或者本地文件系统; Server:配置管理服务端,提供配置文件管理服务,通过server可以按照一定规则读取配置文件,它直接访问配置仓库; Client:配置客户端,即需要从Server读取配置的应用,它并不直接访问配置仓库,而是通过Server读取配置信息; git仓库本身使用频率高,可以进行版本跟踪等特点,所以spring推荐使用git仓库,本文也将重点介绍基于git仓库的配置中心。 2. 配置服务端 服务端支持的配置仓库有git、svn和native本地文件,spring.profiles.active配置项用于启用配置文件支持的配置仓库,默认为git。在使用git配置仓库有几个重要概念: application: 访问配置服务的名称,默认为application,可以通过spring.config.name配置 profile:激活的profile配置文件,多个以逗号分隔 label:通常为git的分支,默认为master,也可以为commit id、tag名称;如果分支或tag名称包含斜线“/”,那么在HTTP URL中需要更换为"(_)" 下边先简单介绍使用本地文件和本地git仓库搭建配置服务端。 2.1. 使用本地文件 1、新建config-server-native工程,引入依赖: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> 2、启动类加上@EnableConfigServer注解,表明该工程为配置服务端 3、新建本地配置仓库文件夹: ``mkdir /Users/sun/Desktop/belonk/config-native`` 在config-native文件夹中建三个文件:application-dev.properties,application-uat.properties,application-pro.properties,分别配置部分内容以区别不同环境(profile)。 4、application.properties添加如下配置: spring.profiles.active=native # 采用推荐的8888端口 server.port=8888 spring.config.name=config # 配置本地文件目录地址 spring.cloud.config.server.native.searchLocations=/Users/sun/Desktop/belonk/config-native spring.cloud.config.server.native.addLabelLocations=true # 配置本地git仓库地址 #spring.cloud.config.server.git.uri=file:///Users/sun/Desktop/belonk/config-center/ spring.profiles.active:默认为git,改为native启用本地文件系统配置仓库 spring.config.name:访问URL的application名称 spring.cloud.config.server.native.searchLocations:配置本地搜索配置文件的位置,默认包括[classpath:/, classpath:/config, file:./, file:./config] 5、启动应用,浏览器访问http://localhost:8888/config/pro,dev,可以看到显示了正确pro和dev的配置信息,需要注意的是这里的config来自spring.config.name配置。 由于本地文件系统没有版本控制,不便于管理和追述,建议仅仅用于测试,不建议生产上使用。 2.2. 使用本地git仓库 git仓库在本地文件系统,并未push到远端,可用于快速测试。我们在config-server-native工程上稍作修改,改为本地git仓库。 ...

2018-08-14 · 2 min · 380 words · Hank

Visio替代者-亿图9.0破解版下载

1. 简介 亿图,一款windows平台的微软Office风格的绘图软件,不仅简单易用,而且拥有强大的功能,支持的图形图表种类非常丰富,所绘制的图形也十分美观,你可以用来轻松绘制各种图形, 如流程图、框图、思维导图、工程设计图、平面图、时装设计等等。 官网: https://www.edrawsoft.com/cn/ 2. Visio和亿图 Visio是微软收购的一款功能强大的绘图软件,但是其门槛较高,没有一定的专业知识和使用经验很难画出漂亮的图形;而亿图提供了直接从visio导入图形文件的功能,可以说对visio进行了一定程度的兼容,而且使用门槛低,使用亿图提供的强大的丰富多样的模板就可以绘制漂亮的图形,简单方便。 简单而言,Visio适合专业人员使用,是一款专业的绘图工具,功能也非常强大,熟练掌握过后能够绘制出非常专业的图形;而亿图专门为非专业人士设计,提供强大多样的模板,普通使用者也可以快速上手,制作符合需要的漂亮图形,简单、方便、高效! 我们以绘制电路图为例,来看看亿图为什么可以替代visio。 (一)、使用Visio画电路图有哪些不足之处? 滑动变阻器不能调节滑片位置,只能在正中间。即使不连接某一侧接线柱,也会显示出来。 Visio可使用的模板和例子有限。 Visio支持的绘图类型少,内置符号也不是很多,绘图时可选择性不高,画出来的图表美感不足。 Visio的云共享功能需要依靠Microsoft Sharepoint才能使用。 Visio不支持钢笔工具的功能。 Visio只能在Windows上使用,不支持Mac和Linux系统。 (二)、亿图软件为什么可以替代Visio? 亿图自带基本电气模板,有常用的电路元件,可直接拖动到图中。 亿图软件具有多种图形建模功能,除了适用软件内置的符号、图形,还可通过修改已有图形形状或用绘图工具绘制建立新图形,甚至可以从外界输入图形放入模板中调用。 拖拽式绘图,只需在亿图符号库中选中所需图形、符号,然后轻轻按住鼠标,拖放到绘图区即可。 亿图内置丰富的符号,适用范围广。无论是思维导图、流程图、组织结构图、项目管理类图,还是工艺图、地图、科技类图,适用亿图都可轻松绘制。 亿图软件自带云共享功能,其中包含个人云和团队云,随时随地保存,随时随地分享! 亿图软件具有PS下相类似的钢笔工具的功能,可以自己设计图形、符号。 操作界面与微软的 Office 系列软件相似,使用简单、方便,无需复杂学习。 具有再次编辑的功能,即使用亿图软件导出为word、ppt、Visio等格式的文件,可以进行再次编辑。 作为一款纯矢量的绘图软件,使用亿图绘制的图表,无论放大多少倍依旧十分清晰。 亿图与Visio完美衔接,可以任意导入和导出为Visio格式的问题件,而且导出后还可以在Visio上继续编辑。 亿图支持跨平台使用,可以在Windows、Mac以及Linux上同时使用。 3. 下载和破解 1、从百度云盘下载: 链接 https://pan.baidu.com/s/1eFC9Lo418qqaSW0p_9hAYw 2、解压压缩包,安装edrawmax-cn-9.0.exe到指定目录 3、将crack文件夹里的2个dll拷贝到亿图安装目录下替换源文件 4、破解完成! 4. 软件部分截图 Figure 1. 新建绘图 Figure 2. 逻辑结构图 Figure 3. 流程图 5. 申明 本站提供的破解软件仅供个人学习用,切勿用于商业用途! 本站不对使用破解软件造成的任何损失承担责任! 经济允许的话,请支持正版软件!

2018-07-31 · 1 min · 57 words · Hank

Spring Cloud简介

1. 简介 微服务的实施,涉及的内容非常多,包括服务治理、配置中心、服务网关、服务熔断、分布式事务、集群、消息总线等等,各个部分开源的产品也非常多: 服务治理:阿里的Dubbo以及当当维护的DubboX、Netflix的Eureka、Apache的Consul等; 配置中心:Spring Cloud的Config、百度的Disconf、Netflix的Archaius、淘宝的Diamond等; 服务跟踪:Spring Cloud的Sleuth、Twitter的Zipkin等 服务网关:Netflix的Zuul等 …… 开源组件如此众多,在实施微服务时,势必带来技术选型方面的困扰,不仅要了解各个技术的作用、易用性、优缺点,还需要知道其他组件对其支持程度,以及组件之间的依赖、协作关系,因此,很多公司在一开始就心生畏惧甚至直接放弃。 为了解决上述问题,Spring Cloud应运而生。它不单独解决微服务某一方面的问题,而是提出了微服务一整套解决方案。Spring Cloud整合了很多开源的微服务产品,针对微服务某一方面的问题,它提供了默认的实现,从而极大的简化了微服务实施的技术难度,使得技术人员不必再关注技术选型方面的问题,大大节约了成本。 简言之,Spring Cloud是一套构建于Spring boot之上的微服务框架,它整合了大部分微服务实施所需的组件,并提供了默认实现,使得复杂的微服务实施变得简单化。官网地址: http://projects.spring.io/spring-cloud/ 2. 特点 Spring Cloud专注于为典型用例提供良好的开箱即用体验,并为其他用户提供可扩展性机制。 分布式/版本化配置 服务注册和发现 路由 服务间调用 负载均衡 断路器 分布式消息 3. 组件 Spring Cloud整合了微服务架构所需的大部分开源组件,包括: Spring Cloud Config 即配置管理工具,由git(也支持svn、本地文件系统等)存储库支持的集中式外部配置管理,实现了配置的外部化存储。 Spring Cloud Netflix 核心组件,整合了Netflix OSS开源的组件,包括许多子组件: Eureka:服务治理组件,包括服务注册、发现,对Netflix的Eureka进行整合; Hystrix:容错管理组件,断路器,对Netflix的Hystrix进行了整合; Ribbon:客户端负载均衡,也是Netflix的开源组件,Spring Cloud进行了整合; Feign:基于Ribbon和Hystrix的声明式服务调用,整合Netflix的Feign; Zuul:网关组件,智能路由、访问过滤,整合Netflix的Zuul; Archaius:外部化配置组件,整合netflix的Archaius; Spring Cloud Bus 轻量级消息总线,用于集群间通信,传播集群间状态改变,例如动态更新配置。 Cloud Foundry 将您的应用程序与Pivotal Cloud Foundry集成。提供服务发现实现,还可以轻松实现受SSO和OAuth2保护的资源。 Spring Cloud Open Service Broker 提供构建实现Open Service Broker API的服务代理的起点。 Spring Cloud Cluster 基于Zookeeper,Redis,Hazelcast,Consul的选举和通用状态模式的抽象实现。 Spring Cloud Consul Hashicorp Consul的服务发现和配置管理。 Spring Cloud Security 为Zuul代理中的负载平衡OAuth2 rest客户端和身份验证头中继提供支持。 Spring Cloud Sleuth Spring Cloud应用程序的分布式跟踪,兼容Zipkin,HTrace和基于日志(例如ELK)的跟踪。 Spring Cloud Data Flow 组合微服务应用程序的本地云编排服务,提供了易用的DSL、拖放式GUI和REST-API共同简化了基于数据管道的微服务整体编排。 Spring Cloud Stream 轻量级事件驱动的微服务框架,可快速构建可连接到外部系统的应用程序。采用声明式模型,在Spring Boot应用程序之间使用Apache Kafka或RabbitMQ发送和接收消息。 Spring Cloud Stream App Starters 基于Spring Boot的Spring应用程序集成,可提供与外部系统的集成。 Spring Cloud Task 一种短命的微服务框架,用于快速构建执行有限数据处理的应用程序。用于向Spring Boot应用程序添加功能和非功能功能的简单声明。 Spring Cloud Task App Starters Spring Cloud Task App Starters是Spring Boot应用程序,可能是任何进程,包括不能永久运行的Spring Batch作业,它们在有限的数据处理期后结束/停止。 Spring Cloud Zookeeper 使用Apache Zookeeper进行服务发现和配置管理。 适用于Amazon Web Services的Spring Cloud 与托管的Amazon Web Services轻松集成。它提供了一种使用众所周知的Spring习语和API(如消息传递或缓存API)与AWS提供的服务进行交互的便捷方式。开发人员可以围绕托管服务构建应用程序,而无需关心基础结构或维护。 Spring Cloud Connectors 使各种平台中的PaaS应用程序可以轻松连接到数据库和消息代理(该项目以前称为“Spring Cloud”)等后端服务。 Spring Cloud CLI Spring Boot CLI插件,用于在Groovy中快速创建Spring Cloud组件应用程序 Spring Cloud Gateway 一款基于Project Reactor的智能可编程路由器。 Spring Cloud OpenFeign 通过自动配置和Spring环境以及其他Spring编程模型习惯用法提供Spring Boot应用程序的集成。 Spring Cloud Function 通过函数促进业务逻辑的实现。它支持无服务器提供商之间的统一编程模型,以及独立运行(本地或PaaS)的能力。 ...

2018-07-20 · 2 min · 275 words · Hank

Spring Boot JPA使用详解

在上一篇SpringBoot-工程结构、配置文件以及打包中,我们介绍了Spring Boot项目的工程结构、基本配置、使用IDEA开发的配置,以及可执行jar包的结构和打包方式,对Spring Boot的项目有了整体的认识。在本篇,我们将介绍JPA的使用。 1. 简介 百度百科对JPA的解释是这样的: JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 JPA由EJB 3.0软件专家组开发,作为JSR-220实现的一部分。但它又不限于EJB 3.0,你可以在Web应用、甚至桌面应用中使用。JPA的宗旨是为POJO提供持久化标准规范。Hibernate3.2+、TopLink 10.1.3以及OpenJPA都提供了JPA的实现。 简单来说,Hibernate这种ORM的持久化框架的出现极大的简化了数据库持久层的操作,随着使用人数的增多,ORM的概念越来越深入人心。因此,JAVA专家组结合Hibernate,提出了JAVA领域的ORM规范,即JPA。 JPQL 其实同Hibernate的HQL类似,是JPA标准的面向对象的查询语言。百度百科的解释如下: JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 JPA由EJB 3.0软件专家组开发,作为JSR-220实现的一部分。但它又不限于EJB 3.0,你可以在Web应用、甚至桌面应用中使用。JPA的宗旨是为POJO提供持久化标准规范。Hibernate3.2+、TopLink 10.1.3以及OpenJPA都提供了JPA的实现。 JPA与Hibernate的关系 JPA是参考Hibernate提出的Java持久化规范,而Hibernate全面兼容JPA,是JPA的一种标准实现。 JPA与Spring Data JPA Spring Boot对JPA的支持其实使用的是Spring Data JPA,它是Spring对JPA的二次封装,默认实现使用的是Hibernate,支持常用的功能,如CRUD、分页、条件查询等,同时也提供了强大的扩展能力。 2. HelloWorld 僚机了JPA的概念过后,接下来,我们使用Spring Boot工程来实现一个最简单的CRUD操作,看看使用JPA我们需要做哪些事情。 引入依赖 直接引入如下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> ...

2018-06-27 · 7 min · 1461 words · Hank

使用TpLink的DDNS让外网访问本地服务

1. 背景 很多时候,我们需要将本地服务暴露给外网访问,或者简单地说能够通过互联网访问本地电脑。比如,公司内网有一台服务器,上边的多个服务都需要在公司和在家里都能访问到,如代码库、数据库等;又比如,在开发微信公众号此类需要外网回调本地环境的应用,如果能够将本地服务暴露给外网访问,那么调试起来将非常方便。 目前,市面上有许多工具软件,能够实现上述目的,但是,一方面稳定性欠佳,另一方面几乎都需要付费,不能作为长期使用。 所以,本文介绍的是,如何使用TpLINK路由器提供的DDNS [1] 功能,改造家里的电信网络,将家里的电脑暴露给外网。 2. 准备 原网络服务商为电信,需要有电信光猫、一台TpLINK路由器(支持DDNS [1])、网线。由于我家里的网络原来都是已经连接好的,电信光猫下接了路由器,通过电信光猫和路由器都可以上网,所以本文是基于此来进行网络配置修改的,全部使用的是笔记本的无限网络访问路由器和光猫,并没有用网线直连光猫和路由器。 3. 整体思路 将电信光猫上网方式由原来的“路由”改为“桥接”,让其仅充当交换机的作用;然后配置TpLink路由器,所有的上网都通过TpLink而不是光猫;然后利用TpLink的DDNS和虚拟服务器功能,将本地端口映射到外网,通过DDNS域名就可以访问本地服务了。 4. 实现步骤 在我的网络环境中,以电信光猫为中心,下边连接了多个路由器,包括一台TpLink路由器,路由器和光猫都可以上网,具体怎么实现上述结构的请查阅相关资料。路由器地址:192.168.0.1,电信光猫地址:192.168.1.1。 4.1. 破解电信光猫 首先,电信光猫默认的useradmin账号登进去的权限很有限,不能修改网络设置。所以,我们需要破解之,为修改上网方式做准备。 使用useradmin和密码登录光猫(用户名和密码在光猫背面,一般为useradmin),然后访问http://192.168.1.1/backupsettings.conf,此时会下载光猫配置文件; 用记事本等文本工具打开,搜索TeleComAccount,然后你会看到如下的配置: <X_CT-COM_TeleComAccount> <Password>8MAB1223</Password> </X_CT-COM_TeleComAccount> Password就是光猫管理用户telecomadmin的密码,记录下来备用。 然后,再搜所Username,找到如下配置: <Username>CD68458918</Username> <Password>aAg0xDgADTgA</Password> 其中,Username就是宽带拨号上网的账号,Password就是其密码,不过是Base64的密文,在线找一个BASE64解码网站,就可以得到原始密码,记录下来备用。 然后,把http://devacs.edatahome.com:9090/ACS-server/ACS找到(只有一处),改成http://devacs.edatahome.com.disable:9090/ACS-server/ACS,使得无法连接远程管理服务器,然后找到 <TotalTerminalNumber>5</TotalTerminalNumber> 把5改成一个比较大的数字用以解除光猫接入设备数量限制。 最后,通过前边的telecomadmin和找到的密码,登录光猫,访问http://192.168.1.1/updatesettings.html,选择刚才修改的配置,让光猫启用最新配置(过程大概需要2分钟)。 ok,光猫破解完成。 4.2. 修改电信光猫桥接上网 光猫完成破解后,通过telecomadmin登录,找到状态菜单-用户侧信息,可以查看到当前WAN连接信息,如下图所示: 找到TpLink路由器连接的对应WAN连接,我这里是上边红框部分,截图是我已经修改成了桥接模式,所以地址获取方式一栏写的是BRIDGE。 然后,点击网络-网络设置,连接名称选择上边确定的TpLink连接的WAN连接,然后将连接模式改为桥接,如下图所示: 其他不用做任何修改。 保存过后,现在光猫是不能上网的,接下来,需要配置路由器上网。 4.3. TpLink宽带拨号上网 访问http://192.168.0.1,登录TpLink,点击左侧上网设置菜单,修改上网方式为宽带拨号上网(原来是默认的自动获得IP地址),如下图: 宽带账号和密码分别填入前边 #netaccount[破解光猫时]找到的宽带账号和密码,然后点击连接按钮。 现在,路由器可以正常上网了。但是由于更改了网络配置,现在不能通过192.168.1.1访问光猫了,要WIFI访问光猫,把路由器的上网方式还原为自动获取IP地址即可。 4.4. TpLink #ddns[DDNS]配置 点击路由器下边的应用管理,找到DDNS,点击进入,如图所示: 看到的界面如下: 服务提供商选择TP-LINK,在我的域名下会显示当前登录的DDNS域名,如果没有,点击创建新域名,然后登录即可,具体DDNS使用可以看 这里。 4.5. TpLink虚拟服务器端口映射 DDNS有了,但是还不能访问内网服务,还需要在路由器上做端口映射,将内网端口暴露到外网。 同样在应用管理,找到虚拟服务器,进入后可以看到如下图所示界面: 外部端口:外网能够访问的端口 内部端口:内部服务的端口 添加完成后,现在通过[DDNS域名]:[外部端口]就可以访问内网服务了,我这里是 http://belonk.tpddns.cn:8088。 大功告成!! 5. 结束语 TpLink目前是自带了DDNS,可以免费申请DDNS域名,免费而且稳定。在使用的时候,需要将TpLink路由器作为上网源,光猫只是充当交换器而不提供路由功能。 1. DDNS(Dynamic Domain Name Server)是动态域名服务的缩写。DDNS是将用户的动态IP地址映射到一个固定的域名解析服务上,用户每次连接网络的时候客户端程序就会通过信息传递把该主机的动态IP地址传送给位于服务商主机上的服务器程序,服务器程序负责提供DNS服务并实现动态域名解析。 ...

2018-06-09 · 1 min · 72 words · Hank

SpringBoot-工程结构、配置文件以及打包

前一篇,我们简单介绍了SpringBoot的概念,然后逐步构建了一个HelloWorld的Web应用。现在,我们来整体认识一下SpringBoot,包括:工程结构、配置文件、可执行Jar包等。 1. 工程结构 本质上而言,SpringBoot是一个遵循Maven标准的Maven工程,但是如果开发的是Web项目,与标准Maven存在一定的差别:默认情况下,SpringBoot打包为jar(简单方便,使用自带容器,不需要部署到tomcat等java容器,当然也可以打包为可执行war),此时,不能使用Maven的webapp目录来存放web资源(js、css、图片、页面等),SpringBoot有一套自己的规范,整个工程结构如下: \---src +---main | +---java // java后台代码 | \---resources // 资源文件目录 | application.properties // 配置文件 | +---static // 存放静态文件,如js、css、图片等 | \---templates // 存放模板文件 \---test \---java // 测试代码 默认情况下,Spring boot规定将静态资源存放到resources/static目录,而页面存放到resources/templates目录,也可以通过配置文件修改默认存放目录。 2. 代码结构 Spring Boot推荐的后台代码结构如下: +---src | +---main | | +---java | | | \---com | | | \---belonk | | | | WebappViewStarter.java // 包含main方法的启用类 | | | +---config | | | | CustomParamConfig.java | | | | RandomParamConfig.java | | | +---domain | | | +---service | | | \---web | | | HomeController.java ...

2018-06-07 · 3 min · 505 words · Hank

Intellij IDEA技巧-正则查询替换和纵向编辑

IntelliJ IDEA是一款非常强大的Java IDE,当然现在也支持多种语言,由JetBrains公司开发。IntelliJ IDEA在业界被公认为最好的java开发工具之一,尤其在智能代码助手、代码自动提示、重构、J2EE支持、各类版本工具(git、svn、github等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超常的。 使用IDEA的感觉就是,一开始可能不习惯(与Eclipse的设计思想完全不同),但是越用就会发现越强大!这里介绍IDEA的正则查询替换和纵向编辑两个非常强大的功能。 1. 正则查找替换 正则查找替换即:支持使用正则表达式来匹配或替换文本,并且可以通过<code>$n</code>来访问正则的分组。 举个例子:我想要将Mapper取值表达式后边的jdbcType=XXX去掉,怎么做?没有正则,那么我们只能使用多次替换。使用正则,那么我们只需将#{xxx,jdbctye=XXX}部分查询出来,并且按分组替换即可,如下图所示: 在IDEA按快捷键CTRL+R开启查找替换功能,输入正则{(w*),(w*=w*)},此时IDEA会自动高亮匹配的内容。然后再替换框中输入{},即替换后的内容保留匹配正则的第一个分组,此时,IDEA会有一个灰色的pop框来告诉你,该内容会被替换成什么内容(见图中的#{rank})。然后,直接点击replace all替换全部替换即可,也可以使用exclude和replace一个个排除和替换。 正则替换功能适用于需要在某些有规律的内容上进行编辑,只要正则能够匹配到这部分内容。 2. 纵向编辑 纵向编辑是我个人非常喜欢的功能之一,其实很多编辑器(例如VIM、SUBLIME)都支持。在IDEA中,要开启纵向编辑,先按住ALT键不松开,然后按住鼠标竖向移动选择即可。 同样举个简单的例子:将设我从某些地方拷贝了一串属性,现在想要将其定义到具体的Class属性中,如图所示: 假设字段类型都是String,我们需要做以下几步: 1、将首字母变为小写 2、在属性前边添加private String 3、在属性后边添加分号 我们看看使用纵向编辑怎么做: 如上图所示,先纵向选择过后,按HOME键,将光标跳转每一行首,然后SHIFT+→选择每一行首字母,再CTRL+SHIFT+U将选中部分转为大小写,然后在每一行前边添加内容,完成后按End键将光标跳转到每一行尾,输入分号,结束。整个过程中,光标始终处于纵向编辑状态。 这里用到了几个小技巧:SHIFT+左右箭头,可以选择一个字符;CTRL+SHIFT+左右箭头,可以选择一个单词;HOME和END分别跳转到行首和行尾。 接下来,我们看一个实际的综合使用这两个功能的例子。 3. 综合实例 3.1. 需求 我从API文档拷贝了请求API的内容片段,现在,我需要为这些字段建立Class,内容片段如下: "ParkOrder_ID": 131391,//订单ID "ParkOrder_OrderNo": "20170209150056708-B11110",//订单号码 "Parking_ID": 1,//停车场ID "Parking_Enable": 1,//停车场状态 "Parking_Key": "e0b3ca87caf3a415bb3b3f52ca8aa795",//停车场编码 "Parking_Name": "城市明珠停车场1",//停车场名字 "Parking_FreeTime": 2,//首次免费分钟 "Parking_FreeTimeout": 2,//超时免费分钟 "ParkOrder_CarNo": "粤B11110",//车牌号码 "ParkOrder_CarType": "3651",//停车缴费类型 "CarType_No": "3651",//车辆类型 "CarType_Name": "临时车",//车辆类型名字 "ParkOrder_EnterTime": "2017-02-09 15:00:56",//入场时间 "ParkOrder_EnterGateName": "入口车道1",//入口车道名字 "ParkOrder_EnterOperatorName": "管理员",//入场操作员 "ParkOrder_EnterImgPath": "/OrderImg/e0b3ca87caf3a415bb3b3f52ca8aa795/20170209/20170209150056708-B11110-0.jpg",//图片URL "ParkOrder_OutTime": null,//出场时间 "ParkOrder_OutGateName": null,//出口车道名字 "ParkOrder_OutOperatorName": null,//出口操作员名字 "ParkOrder_OutImgPath": null,//出口图片URL "ParkOrder_TotalAmount": null,//总金额 "ParkOrderStatus_No": "200",//停车状态200为 车辆入场 201为车辆出场 202为自动关闭 "ParkOrderStatus_Name": "已入场",//状态中文名字 "ParkOrder_Lock": 0,//车辆是否锁定 "WXUser_ID": null,//微信ID "WXUser_Openid": null,//微信openid "WXUser_Nickname": null,//微信nikename "WXUser_Headimg": null,//头像url "PayOrder_Status": "未支付",//支付状态 "PayOrder_PayedMoney": 0.0//支付金额 ...

2018-05-31 · 2 min · 394 words · Hank

微服务架构的理论基础 - 康威定律

可能出乎很多人意料之外的一个事实是,微服务很多核心理念其实在半个世纪前的一篇文章中就被阐述过了,而且这篇文章中的很多论点在软件开发飞速发展的这半个世纪中竟然一再被验证,这就是康威定律。 1. 概述 微服务是最近非常火热的新概念,大家都在追,也都觉得很对,但是似乎没有很充足的理论基础说明这是正确的,给人的感觉是 不明觉厉 。前段时间看了Mike Amundsen《远距离条件下的康威定律----分布式世界中实现团队构建》(是Design RESTful API的作者)在InfoQ上的一个分享,觉得很有帮助,结合自己的一些思考,整理了该演讲的内容。 可能出乎很多人意料之外的一个事实是,微服务很多核心理念其实在半个世纪前的一篇文章中就被阐述过了,而且这篇文章中的很多论点在软件开发飞速发展的这半个世纪中竟然一再被验证,这就是康威定律(Conway’s Law). 在康威的这篇文章中,最有名的一句话就是: Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations. — Melvin Conway(1967) 中文直译大概的意思就是:设计系统的组织,其产生的设计等同于组织之内、组织之间的沟通结构**。看看下面的图片,再想想Apple的产品、微软的产品设计,就能形象生动的理解这句话。 用通俗的说法就是:组织形式等同系统设计。 这里的系统按原作者的意思并不局限于软件系统。据说这篇文章最初投的哈佛商业评论,结果程序员屌丝的文章不入商业人士的法眼,无情被拒,康威就投到了一个编程相关的杂志,所以被误解为是针对软件开发的。最初这篇文章显然不敢自称定律(law),只是描述了作者自己的发现和总结。后来,在Brooks Law著名的人月神话中,引用这个论点,并将其“吹捧”成了现在我们熟知“康威定律”。 2. 康威定律 Mike从他的角度归纳这篇论文中的其他一些核心观点,如下: 第一定律 Communication dictates design 组织沟通方式会通过系统设计表达出来 第二定律 There is never enough time to do something right, but there is always enough time to do it over 时间再多一件事情也不可能做的完美,但总有时间做完一件事情 第三定律 There is a homomorphism from the linear graph of a system to the linear graph of its design organization 线型系统和线型组织架构间有潜在的异质同态特性 ...

2018-05-29 · 2 min · 252 words · Hank

什么是微服务架构

在过去几年中,“微服务架构”这一术语如雨后春笋般涌现出来,它描述了一种将软件应用程序设计为一组可独立部署的服务的特定方式。虽然这种架构风格没有明确的定义,但在组织、业务能力上有一些共同的特征:自动化部署,端点智能化,语言和数据的去中心化控制。 “微服务” - 软件架构拥挤大街上的有一个新术语。虽然我们自然的倾向是轻蔑的一瞥将它一带而过,然而我们发现这一术语描述了一种越来越吸引人的软件系统风格。我们已看到,在过去的几年中有许多项目使用了这种风格,并且到目前为止结果都还不错,以致于这已变成了我们同事在构建企业级应用程序时默认使用的架构风格。然而,遗憾的是并没有太多的信息来概述什么是微服务风格以及怎样用这种风格。 简单来说,微服务架构风格 [1] 是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。 与单体风格作对比有助于开始解释微服务风格:单体应用程序被构建为单一单元。企业级应用程序通常由三部分组成:客户端侧用户接口(由运行于开发机上的浏览器里的HTML页面和Javascript组成),数据库(由插入到通用关系型数据库管理系统中的许多数据表格组成),服务端应用程序。服务端应用程序处理HTTP请求,执行领域逻辑,从数据库中检索、更新数据,选择、填充将要发送到浏览器的HTTP视图。服务端应用程序是一个单一的逻辑可执行单体 [2] 。系统的任何改变都将牵涉到重新构建和部署服务端的一个新版本。 这样的单体服务器是构建这样一个系统最自然的方式。处理请求的所有逻辑都运行在一个单一进程中,允许你使用编程语言的基本特性将应用程序划分类、函数和命名空间。你认真的在开发机上运行测试应用程序,并使用部署管道来保证变更已被正确地测试并部署到生产环境中。该单体的水平扩展可以通过在负载均衡器后面运行多个实例来实现。 单体应用程序可以是成功的,但人们日益对他们感到挫败,尤其是随着更多的应用程序被部署在云上。变更周期被捆绑在一起 —— 即使只变更应用程序的一部分,也需要重新构建并部署整个单体。长此以往,通常将很难保持一个良好的模块架构,这使得很难变更只发生在需要变更的模块内。程序扩展要求进行整个应用程序的扩展而不是需要更多资源的应用程序部分的扩展。 Figure 1. 单体和微服务 这些挫败导向了微服务架构风格:构建应用程序为服务套件。除了服务是可独立部署、可独立扩展的之外,每个服务都提供一个固定的模块边界。甚至允许不同的服务用不同的的语言开发,由不同的团队管理。 我们不会声称微服务风格是新颖的、创新的,其本质至少可以回溯到Unix的设计哲学。但我们的确认为没有足够的人仔细考虑微服务架构,并且如果使用它很多软件实现将会更好。 1. 微服务架构的特征 我们无法给出微服务架构风格的一个正式定义,但我们可以尝试去描述我们看到的符合该架构的一些共性。就概述共性的任何定义来说,并非所有的微服务架构风格都有这些共性,但我们期望大多数微服务架构风格展现出大多数特性。虽然本文作者一直是这个相当松散的社区的活跃用户,我们的目的是试图描述我们工作中和我们知道的一些团队的相似努力中的所见所闻。特别是我们不会制定一些可遵守的定义。 1.1. 通过服务组件化 只要我们一直从事软件行业,一个愿望就是通过把组件插在一起构建系统,如同我们看到的现实世界中事物的构造方式一样。在最近的二十年中,我们看到作为大多数语言平台一部分的公共库的大量汇编工作取得了很大的进展。 当谈到组件时,我们遭遇困难的定义:组件是什么。我们的定义是:组件是一个可独立替换和独立升级的软件单元。 微服务架构将使用库,但组件化软件的主要方式是分解成服务。我们把库定义为链接到程序并使用内存函数调用来调用的组件,而服务是一种进程外的组件,它通过web服务请求或rpc(远程过程调用)机制通信(这和很多面向对象程序中的服务对象的概念是不同的 [3] 。) 使用服务作为组件而不是使用库的一个主要原因是服务是可独立部署的。如果你有一个应用程序 [4] 是由单一进程里的多个库组成,任何一个组件的更改都导致必须重新部署整个应用程序。但如果应用程序可分解成多个服务,那么单个服务的变更只需要重新部署该服务即可。当然这也不是绝对的,一些变更将会改变服务接口导致一些协作,但一个好的微服务架构的目的是通过内聚服务边界和按合约演进机制来最小化这些协作。 使用服务作为组件的另一个结果是一个更加明确的组件接口。大多数语言没有一个好的机制来定义一个明确的 发布接口。通常只有文档和规则来预防客户端打破组件的封装,这导致组件间过于紧耦合。服务通过明确的远程调用机制可以很容易的避免这些。 像这样使用服务确实有一些缺点,远程调用比进程内调用更昂贵,因此远程API被设计成粗粒度,这往往更不便于使用。如果你需要更改组件间的责任分配,当你跨进程边界时,这样的行为动作更难达成。 直观的估计,我们观察到服务与运行时进程一一映射,但这仅仅是直观的估计而已。一个服务可能由多进程组成,这些进程总是被一起开发和部署,比如只被这个服务使用的应用进程和数据库。 1.2. 围绕业务能力组织 当想要把大型应用程序拆分成部件时,通常管理层聚焦在技术层面,导致UI团队、服务侧逻辑团队、数据库团队的划分。当团队按这些技术线路划分时,即使是简单的更改也会导致跨团队的时间和预算审批。一个聪明的团队将围绕这些优化,两害取其轻 - 只把业务逻辑强制放在它们会访问的应用程序中。换句话说,逻辑无处不在。这是Conway法则 [5] 在起作用的一个例子。 任何设计系统(广泛定义的)的组织将产生一种设计,他的结构就是该组织的通信结构。 — Melvyn Conway 1967 Figure 2. Conway法则在起作用 微服务采用不同的分割方法,划分成围绕业务能力组织的服务。这些服务采取该业务领域软件的宽栈实现,包括用户接口、持久化存储和任何外部协作。因此,团队都是跨职能的,包括开发需要的全方位技能:用户体验、数据库、项目管理。 Figure 3. 团队边界增强的服务边界 www.comparethemarket.com是按这种方式组织的一个公司。跨职能团队负责创建和运营产品,产品被划分成若干个体服务,这些服务通过消息总线通信。 大型单体应用程序也总是可以围绕业务能力来模块化,虽然这不是常见的情况。当然,我们将敦促创建单体应用程序的大型团队将团队本身按业务线拆分。我们看到这种情况的主要问题是他们趋向于围绕太多的上下文进行组织。如果单体横跨了多个模块边界,对团队个体成员来说,很难把它们装进他们的短期记忆里。另外,我们看到模块化的路线需要大量的规则来强制实施。服务组件所要求的更加明确的分离,使得它更容易保持团队边界清晰。 侧边栏:微服务有多大? 虽然,“微服务”已成为这种架构风格的代称,这个名字确实会导致不幸的聚焦于服务的大小,并为“微”由什么组成争论不休。在与微服务实践者的对话中,我们发现有各种大小的服务。最大的服务报道遵循亚马逊两匹萨团队(也就是,整个团队吃两个披萨就吃饱了)的理念,这意味着团队不超过12个人。在更小的规模大小上,我们看到这样的安排,6人团队将支持6个服务。 这导致这样一个问题,在服务每12个人和服务每1个人的大小范围内,是否有足够打的不同使他们不能被集中在同一微服务标签下。目前,我们认为最好把它们组合在一起。但随着深入探索这种风格,我们一定有可能改变我们的看法。 1.3. 是产品不是项目 我们看到大多数应用程序开发工作使用一个项目模式:目标是交付将要完成的一些软件。完成后的软件被交接给维护组织,然后它的构建团队就解散了。 微服务支持者倾向于避免这种模式,而是认为一个团队应该负责产品的整个生命周期。对此一个共同的启示是亚马逊的理念 “you build, you run it” ,开发团队负责软件的整个产品周期。这使开发者经常接触他们的软件在生产环境如何工作,并增加与他们的用户联系,因为他们必须承担至少部分的支持工作。 产品思想与业务能力紧紧联系在一起。要持续关注软件如何帮助用户提升业务能力,而不是把软件看成是将要完成的一组功能。 没有理由说为什么同样的方法不能用在单体应用程序上,但服务的粒度更小,使得它更容易在服务开发者和用户之间建立个人关系。 ...

2018-05-29 · 2 min · 343 words · Hank

Spring Boot之基础入门

最近在了解微服务和Spring Cloud相关的东西,Spring Cloud的微服务体系基于Spring boot,所以了解Spring Boot是前提和基础。本文将介绍Spring Boot基础内容,包括简介、环境搭建、示例代码等,所使用JDK版本为JDK8,构建工具为Maven3.5。 1. 简介 Spring Boot是Spring的开源项目,其目的是让开发者可以简单、快速地使用Spring框架创建生产级的应用,其目的不是为了替换Spring,而是简化Spring的使用。Spring Boot可以看做是对Spring框架的二次封装,不过,在封装的同时也还提供了许多高级功能。 Spring Boot特性如下: 创建独立的Spring应用程序 直接嵌入Tomcat、Jetty或Undertow(无需部署WAR文件) 为项目构建提供了许多的“starter”,适用于整合不同的框架,从而简化您的构建工具(Maven)配置 尽可能自动配置Spring 提供可生产的特性,如度量指标、健康检查和集中配置 绝对没有代码生成,也不需要XML配置 2. 系统需求 Spring Boot需要JDK8或以上版本,嵌入的tomcat8.5所支持的Servlet版本为3.1,如下表所示: 依赖 版本 JDK 8或以上版本 Maven 3.2或以上版本 Servlet 3.1+ 3. 构建应用 现在,我们来一步步构建一个Hello world应用。 3.1. 构建 Spring Boot项目是基于Maven构建,完全遵循Maven的项目构建规则和目录规范。所以,你可以直接使用开发工具或者maven命令行来创建Spring Boot项目。需要注意的饿是,如果是Web项目,而且打包的格式为jar,那么与标准Maven项目不同的是不能使用src/main/webapp目录。 也可以访问 https://start.spring.io/来快速构建应用,选择对应的构建工具、语言和版本信息,填写开发信息并加入依赖下载,然后导入开发工具即可: 3.2. 配置Pom.xml 应用创建好了,我们来配置pom.xml,引入Spring boot相关的依赖。 1、继承parent节点 Spring为我们提供了顶层的parent节点,用来定义Spring Boot使用的版本,配置如下: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.10.RELEASE</version> <relativePath/> </parent> ...

2018-05-22 · 2 min · 327 words · Hank

XMind8 pro版本下载和破解

1. 简介 XMind是一款比较易用的思维导图软件,开源且功能强大,有免费版本,能够满足基本使用。如果需要使用更高级的功能,可以付费购买收费版本。 官网: https://www.xmind.net/ 2. 下载和破解 1、从官网下载Xmind8 pro,或者可以从百度云盘下载: 链接 https://pan.baidu.com/s/1OkSXoqcZA4unFbR1Crzl4w 密码 wat9 2、安装软件到指定目录 3、下载破解文件: 链接 https://pan.baidu.com/s/1KJ0hkB_rl4qv1MS3a8L1Aw 密码 bgv4 4、开始破解,步骤如下: (1)将破解文件XMindCrack.jar复制到安装根目录, 如: D:\Program Files\XMind 8 Update 32,以文本格式打开安装目录中XMind.ini (2)在 XMind.ini 最后追加: -javaagent:D:/Program Files/XMind 8 Update 3/XMindCrack.jar (3)打开C:\Windows\System32\drivers\etc路径,在hosts最后添加 127.0.0.1 www.xmind.net点击保存 (4)断网情况下,打开XMind8→帮助→序列号→输入序列号,邮箱可以随便填写,序列号在附件内,点击注册即可完成破解! (5)打开 XMind, 点击帮助----序列号,然后输入以下序列号 ,邮箱任意填写: XAka34A2rVRYJ4XBIU35UZMUEEF64CMMIYZCK2FZZUQNODEKUHGJLFMSLIQMQUCUBXRENLK6NZL37JXP4PZXQFILMQ2RG5R7G4QNDO3PSOEUBOCDRYSSXZGRARV6MGA33TN2AMUBHEL4FXMWYTTJDEINJXUAV4BAYKBDCZQWVF3LWYXSDCXY546U3NBGOI3ZPAP2SO3CSQFNB7VVIY123456789012345 5、破解完成,可以使用任意的Pro版本的功能了,授权信息如下: 此时XMind是无法联网使用的,如果需要使用诸如在线图标的功能,需要恢复联网,即取消hosts的修改内容,建议关闭软件自动更新。 3. 对比分析 之前博主曾介绍过另一款 思维导图软件MindManager,个人觉得也非常强大易用,只是没有中文版本(较早的版本有中文版,喜欢的可以查阅相关资料),这两者的文字对比分析可以看 这里,这里不再详述。 有大神对三款思维导图软件做了详细分析对比,见下图: 图片来源: https://www.zhihu.com/question/22094277 4. 申明 本站提供的破解软件仅供个人学习用,切勿用于商业用途! 本站不对使用破解软件造成的任何损失承担责任! 经济允许的话,请支持正版软件!

2018-05-11 · 1 min · 59 words · Hank

使用visualvm监控Java程序性能三——浏览堆dump文件

堆dump,即堆转储,其实是一个当前JVM堆内存所有对象在某个时间点上的快照。堆dump用来将当前应用程序的堆内存中的数据信息保存为文件,并可以快速浏览文件中的内容,查询对象分配信息。 在VisualVM中,堆dump的入口很多,可以再应用程序右键,选择堆dump;也可以打开主窗口的“监视”tab页,点击右上角有堆dump按钮;还可以打开抽样器tab页,进行内存抽样后,点击工具栏上的堆dump按钮。 1. 打开堆dump 执行完堆dump后,主窗口会打开一个堆dump的页面,同时应用程序节点会增加一个headdump+时间子节点,可以直接打开该节点来浏览堆dump文件信息,如图所示: 堆dump文件存储后缀为.hprof,除了能够看到本地堆dump文件,也可以通过菜单栏的文件-装入打开他人分享的dump文件: 2. 浏览堆dump文件信息 堆dump tab页包含了几个子标签页面:概要、类、实例、OQL控制台,我们重点说下前三个。 2.1. 概要 显示了转储堆dump时的信息,包括基本信息、环境信息、系统属性和线程信息,例如你可以看到堆dump时间、文件存放位置、大小等。 2.2. 类 类视图显示类的列表,以及该类引用的实例的数量和百分占比、类所有实例的大小和百分占比。您可以通过在实例视图中右键单击名称和选择“在实例图中显示”或者直接双击类来查看特定类的实例列表: 通过类视图,可以整体上明确哪些类实例数多,占用资源高。 可以通过最下方的“类目过滤器”来搜索类,点击漏斗形状的图标可以选择筛选类型,例如包含/不包含类名称、正则匹配、搜索子类等。例如,搜索类名包含EsSyncTask的所有类,如下图所示: 2.3. 实例数 当在类视图选择查询具体某一个类的实例时(右键或者双击),此时就打开了该类的实例视图。当您从实例窗格中选择一个实例时,VisualVM将显示该类的字段,并在各自的窗格中引用该类。 除了显示了具体的类和概要信息(实例数量、大小、总大小等)外,实例视图还有几个面板: 实例数:显示当前类的实例列表,点击具体的某一个实例可以在字段和引用面板查看实例的字段信息和引用了的对象 字段:显示当前选择的实例的具体字段,包括字段名称、类型、字段值等 引用:递归显示当前所有引用了该类的类实例,该面板会一层层递归显示引用,包括引用实例的再引用。通过引用面板,可以跟踪对象的引用情况,以便找到具体的类,从而优化代码。 通过右键点击实例或者在引用面板右键,可以查看最近垃圾回收根节点。 接下来,我们来编一个实例程序,看看堆dump的情况,代码如下: public class HeaddumpTest { public static void main(String[] args) { Refrence ref = new Refrence(); int id = 0; while (true) { ref.add(new MyClass(id++, "myclass" + id)); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Refrence { private List<MyClass> myClasses = new ArrayList<>(); public void add(MyClass myClass) { myClasses.add(myClass); } public List<MyClass> getMyClasses() { return myClasses; } public void setMyClasses(List<MyClass> myClasses) { this.myClasses = myClasses; } } class MyClass { private int id; private String name; MyClass(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ...

2018-04-24 · 1 min · 168 words · Hank

使用visualvm监控Java程序性能二——主窗口功能详解

无论是本地还是远程节点,双击或者右键打开应用程序,会直接在主窗口打开程序监控页。主窗口包括几个子页面:概述、监视、线程、抽样器,如果是本地程序且支持分析器还会显示Profiler页,根据安装插件的不同,还可能会显示一些插件的页面,例如MBeas等。 Figure 1. 主窗口界面 1. 概述页 概述页显示了应用程序和运行时环境的基本信息,如下图所示: Figure 2. 概述页界面 概述信息包括基本参数、保存的数据、详细信息几个部分。 1.1. 基本参数 基本参数描述了应用程序的一些参数信息,包括如下信息: PID:应用程序的进程ID 主机:应用程序运行的系统地址 主类:运行了main方法的类 参数:应用启动时所传递的参数信息,例如传递给main方法的参数列表 JVM:当前的JVM信息 Java:当前使用的JDK信息 Java Home:JDK的位置 JVM标志:启动JDK时JVM使用的的标志 出现OOME时生产堆dump:当前出现OOME时生产堆dump功能的开启/禁用状态 除了基本信息外,还包括两个可以自主选择显示或隐藏的功能(右上角):保存的数据和详细信息。 1.2. 保存的数据 显示VisualVM存储的当前应用程序的信息,例如线程dump的数量、堆dump和快照的数量等。 1.3. 详细信息 包括两个标签:JVM参数和系统属性。 JVM参数:配置的JVM启动的参数信息,例如堆大小; 系统属性:JVM运行的系统属性,例如用户目录、文件编码; 2. 监视页 监视tab页展示了监听了当前应用程序的整体情况,包括几个指标:CPU、内存、类、线程,它们都以直观的图形方式展现。 Figure 3. 监视页界面 2.1. CPU 该图展示了CPU的使用百分比走势,包括执行垃圾回收活动的时间等。可以从该图查看应用程序是否耗费CPU,是否频繁的进行垃圾回收,以便优化代码或者调整JVM内存设置。 2.2. 内存 反映了内存的占用情况,包括内存大小、最大值和已经使用的大小。内存情况又包括两部分:堆和Metaspace。 堆:该页展示了堆内存的大小和堆内存使用走势情况。 Metaspace:即元空间,JDK8移除了永久代(PermGen),而类的元数据信息被存储在了Metaspace,具体请看 这里。该标签反映了元空间内存的使用情况。 2.3. 类 类视图显示了已经加载的类数量和共享类的数量走势情况。 2.4. 线程 线程视图显示了应用程序在JVM中生存和守护线程的数量走势情况。如果您想在特定的时间点捕获和查看应用程序线程的精确数据,可以打开线程标签页,使用VisualVM进行线程转储(稍后详述)。 除了这四个图表,监视页还有两个重要的功能:执行垃圾回收和堆dump。 执行垃圾回收:立即触发垃圾回收 堆dump:执行堆dump,并在新的标签页打开,以查看对dump的详细信息(同右键应用程序–堆dump)。 通常,需要长期监控应用程序的情况,幸好这不会带来太大的开销。 我们看一个OOM的例子,看看VisualVM的监视页的监控情况。代码如下: public static void main(String[] args) { List<Object> objects = new ArrayList<>(); new Thread(() -> { while (true) { objects.add(new Object()); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } ...

2018-04-21 · 2 min · 402 words · Hank

使用VisualVM监控Java程序性能一——简介

Java应用程序难免遇到性能问题,例如常见的OutOfMemmoryError,又或者是出现内存泄漏的错误,又或是程序运行一段时间就卡死了,CPU或者内存占用率高,甚至造成系统崩溃,等等。出现性能问题时,往往难以分析和跟踪。不过,可以借助于一些性能分析工具,来监测程序性能,从而找出影响性能的问题所在,以便进行优化。本文的VisualVM就是一款比较强大的性能分析工具。 先来看看性能分析的几种方式: 性能分析的主要方式 性能分析常用的有以下几种方式 * 监视:监视是一种用来查看应用程序运行时行为的一般方法。通常会有多个视图(View)分别实时地显示 CPU 使用情况、内存使用情况、线程状态以及其他一些有用的信息,以便用户能很快地发现问题的关键所在。 * 转储(dump):性能分析工具从内存中获得当前状态数据并存储到文件用于静态的性能分析。Java 程序是通过在启动 Java 程序时添加适当的条件参数来触发转储操作的。它包括以下三种: 核心dump:JVM 生成的本地系统的转储。一般的,系统转储数据量大,需要平台相关的工具去分析,如 Windows 上的 windbg 和 Linux 上的 gdb。 Jvm dump:JVM 内部生成的格式化后的数据,包括线程信息,类的加载信息以及堆的统计数据。通常也用于检测死锁。 堆dump:JVM 将所有对象的堆内容存储到文件。 * 快照:应用程序启动后,性能分析工具开始收集各种运行时数据,其中一些数据直接显示在监视视图中,而另外大部分数据被保存在内部,直到用户要求获取快照,基于这些保存的数据的统计信息才被显示出来。快照包含了应用程序在一段时间内的执行信息,通常有 CPU 快照和内存快照两种类型。 CPU 快照:主要包含了应用程序中函数的调用关系及运行时间,这些信息通常可以在 CPU 快照视图中进行查看。 内存快照:主要包含了内存的分配和使用情况、载入的所有类、存在的对象信息及对象间的引用关系等。这些信息通常可以在内存快照视图中进行查看。 * 性能分析:性能分析是通过收集程序运行时的执行数据来帮助开发人员定位程序需要被优化的部分,从而提高程序的运行速度或是内存使用效率,主要有以下三个方面: CPU 性能分析:CPU 性能分析的主要目的是统计函数的调用情况及执行时间,或者更简单的情况就是统计应用程序的 CPU 使用情况。通常有 CPU 监视和 CPU 快照两种方式来显示 CPU 性能分析结果。 内存性能分析:内存性能分析的主要目的是通过统计内存使用情况检测可能存在的内存泄露问题及确定优化内存使用的方向。通常有内存监视和内存快照两种方式来显示内存性能分析结果。 线程性能分析:线程性能分析主要用于在多线程应用程序中确定内存的问题所在。一般包括线程的状态变化情况,死锁情况和某个线程在线程生命期内状态的分布情况等 — 使用 VisualVM 进行性能分析及调优 https://www.ibm.com/developerworks/cn/java/j-lo-visualvm 1. VisualVM简介 VisualVM是集成JDK命令行工具和轻量级分析功能的可视化分析工具,设计用于开发和生产时间的使用。它提供了一个可视化界面,用于查看基于Java技术、运行于JVM上的应用程序(Java应用程序)的详细信息。 VisualVM组织Java开发工具包(JDK)工具检索的JVM软件的数据,并以一种使您能够快速查看多个Java应用程序的数据的方式提供信息。您可以查看在远程主机上运行的本地应用程序和应用程序的数据。您还可以捕获有关JVM软件实例的数据,并将数据保存到您的本地系统中,以供后期查看或与其他用户共享。 VisualVM完全免费,它通过 jvmstat、JMX、SA(Serviceability Agent)以及 Attach API 等多种方式从程序运行时获得实时数据,从而进行动态的性能分析。同时,它能自动选择更快更轻量级的技术尽量减少性能分析对应用程序造成的影响,提高性能分析的精度。 2. VisualVM安装 从JDK6开始,VisualVM已经成为JDK自带工具,名称叫做jvisualvm,位于%JAVA_HOME%/bin/jvisualvm.exe,执行即可启动。 需要注意的是,从Oracle JDK 9开始,Java VisualVM迁移到GraalVM,这是在Oracle实验室开发的一种创新的、高性能的多语言虚拟机。详情请参阅 Graal VisualVM页面。 ...

2018-04-20 · 1 min · 114 words · Hank

RabbitMQ服务器管理(二)——权限管理

RabbitMQ有用一套专门的权限控制系统,用来控制不同用户对不同虚拟主机的访问控制。基本思路同大多系统一样:先创建用户,然后为用户授予权限。 大多数系统并没有严格区分认证和鉴权,但是在RabbitMQ中,对这两个概念做了明确的区分: 认证:识别用户身份,即:识别当前用户,认证其身份信息 鉴权:明确了用户身份,那么授权就是要检查该用户是否拥有相应的权限,即:检查授予用户的权限 1. 虚拟主机和guest用户 在 RabbitMQ基础(七)----虚拟主机vhost一篇,我们已经详细介绍了虚拟主机。我们说过,首次安装好RabbitMQ时,会拥有一个默认的虚拟主机“/”。同时,还有一个默认的用户“guest”,密码也为“guest”,该用户有用默认虚拟主机的全部权限。 如果你得RabbitMQ需要公网访问,出于安全性考虑,官方建议删掉该用户或者修改其密码。默认情况下,RabbitMQ禁止guest用户远程访问,只可以访问本地的mq服务。这个是通过loopback_users配置项决定的,如果需要取消该限制,仅需将该选项配置为none即可: loopback_users = none 如果是3.7之前的版本,还不支持这样key=value的配置格式,那么你需要配置成这样: ``[{rabbit, [{loopback_users, []}]}].`` 该配置想的意思是,取消所有用户的本地访问限制。按照 官方的示例配置文件,如果仅需要取消guest用户本地访问限制,那么进行以下配置: loopback_users.guest = false 否则设置为true即可。 关于MQ的配置文件,我们将在后续博文中详细讨论,官方文档见 这里。 2. 权限工作机制 当客户端与服务端建立连接时,第一级权限控制将会执行:服务器会检查连接的用户是否有用访问其连接的虚拟主机的权限,没有则会拒绝连接,否则连接建立成功。 用户操作虚拟主机的资源(路由器、队列、绑定等)时,RabbitMQ会启用第二级权限控制,验证用户是否具有访问虚拟机的资源的权限。 2.1. 权限定义 具体而言,在RabbitMQ中,对资源的操作定义了三种权限: 配置:创建和删除资源,或者改变它们的行为; 写:发布消息到资源; 读:从资源获取消息; 下表显示了对执行权限检查的所有AMQP命令所需的资源类型的权限: AMQP 0-9-1 Operation configure write read exchange.declare (passive=false) exchange exchange.declare (passive=true) exchange.declare (with AE) exchange exchange (AE) exchange exchange.delete exchange queue.declare (passive=false) queue queue.declare (passive=true) queue.declare (with DLX) queue exchange (DLX) queue queue.delete queue exchange.bind exchange (destination) exchange (source) exchange.unbind exchange (destination) exchange (source) queue.bind queue exchange queue.unbind queue exchange basic.publish exchange basic.get queue basic.consume queue queue.purge queue 说明: ...

2018-04-12 · 3 min · 504 words · Hank

RabbitMQ服务器管理(一)——ubuntu上安装MQ

在本篇,我们将在ubuntu上安装RabbitMQ,其他操作系统类似。 我们知道,RabbitMQ使用Erlang语言开发,所以需要先安装Erlang语言,在实践过程中,linux安装RabbitMQ还比较麻烦,涉及到很多依赖包的安装,官方的安装文档见 这里。 1. 正确的安装步骤 简单而言,正确的安装步骤如下: 1、安装erlang依赖包 wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb sudo dpkg -i erlang-solutions_1.0_all.deb 编辑/etc/apt/sources.list,添加以下地址的任意一个 deb https://packages.erlang-solutions.com/ubuntu trusty contrib deb https://packages.erlang-solutions.com/ubuntu saucy contrib deb https://packages.erlang-solutions.com/ubuntu precise contrib 然后执行: wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb sudo dpkg -i erlang-solutions_1.0_all.deb 2、安装erlang sudo apt-get update sudo apt-get install esl-erlang(或erlang) 3、下载RabbitMQ wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.7.12/rabbitmq-server_3.7.12-1_all.deb 4、安装RabbitMQ依赖包 apt-get install socat get-get install init-system-helpers get-get install adduser get-get install logrotate 5、安装RabbitMQ dpkg -i rabbitmq-server_3.7.12-1_all.deb 具体我的安装流程以及遇到的问题记录如下: 2. 安装过程 2.1. 下载安装包 ubuntu系统基于debian,所以我们要下载官方给的deb安装包: wget https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.4/rabbitmq-server_3.7.4-1_all.deb 等待下载完成。 2.2. 尝试安装 下载安装后,尝试直接安装: ...

2018-04-12 · 2 min · 255 words · Hank

RabbitMQ基础(七)——虚拟主机vhost

1. 简介 RabbitMQ是一个多租户系统:连接、交换器、队列、绑定、用户权限、策略和其他的东西都属于虚拟主机(virtual hosts,v_host),他们是整个RabbitMQ的逻辑分组。 虚拟主机类似于 Apache的虚拟主机和 Nginx的server块,最重要的区别是:Apache的虚拟主机通过配置文件定义,然而在RabbitMQ中,RabbitMQ只能通过rabbitmqctl控制台工具或者HTTP API来创建。 1.1. 逻辑和物理分隔 如果没有虚拟主机,当RabbitMQ中的数据越来越庞大,队列越来越多,随之而来的是令人头痛的管理问题,比如队列、交换器命名冲突,它们相互影响等等。虚拟主机能够解决这些问题,而不需要我们部署多个RabbitMQ来负责不同的业务。 虚拟主机提供了资源的逻辑分组和分隔,每一个虚拟主机本质上是mini版的RabbitMQ服务器,他们有用自己的连接、队列、绑定、交换器,更重要的是有用自己的权限机制,这有点类似服务器和运行在服务器上的虚拟机一样。 1.2. 客户端连接 通队列、交换器一样,虚拟主机必须定义名称。当AMQP 0-9-1的客户端连接到RabbitMQ时,需要指定虚拟主机名称,同时还需要提供用户名和密码,只有用户具有相关的权限才能建立连接(关于权限控制的详细信息可以看 这里)。 通常,一个虚拟主机的连接只能操作属于该虚拟主机的交换器、队列和绑定等等内容。但是,当客户端同时连接了多个虚拟主机时,可能产生多个虚拟主机内的交换器、队列相互连接的情况,这种情景典型的例子是虚拟主机处于不同的RabbitMQ集群或者同一个集群中, RabbitMQ Shovel plugin就是一个这种场景应用的实例。 1.3. 虚拟主机和STOMP、MQTT 同AMQP 0-9-1一样,STOMP协议也有虚拟主机的概念,具体参见 这里。 相反,MQTT并不支持虚拟主机,MQTT连接默认使用单一的RabbitMQ主机,有MQTT特定的约定和特性,使客户机能够连接到特定的虚拟主机,而无需修改任何客户端lib库。有关详细信息,请参阅 MQTT指南。 2. 虚拟主机管理 RabbitMQ包含一个默认的虚拟主机:“/”,我们默认操作的都是这个虚拟主机,其用户名和密码默认都是guest,为了安全起见我们应该修改其密码(后续文章将详细介绍权限管理)。 2.1. 查询 命令: rabbitmqctl list_vhosts 该命令会查询当前RabbitMQ服务中所有的虚拟机,由于我没有创建任何虚拟机,所以只能看到默认的: rabbitmqctl list_vhosts 2.2. 创建 命令: rabbitmqctl list_vhosts 创建时必须指定名称: rabbitmqctl list_vhosts 创建完成后,查询: rabbitmqctl list_vhosts 可以看到刚创建的虚拟主机。 2.3. 删除 命令: rabbitmqctl list_vhosts 同样必须指定要删除的vhost的名称: rabbitmqctl list_vhosts 查询: rabbitmqctl list_vhosts ...

2018-04-04 · 1 min · 140 words · Hank

windows全能终端神器MobaXterm9.4破解版下载

1. 简介 MobaXterm是远程计算的终极工具箱。在一个单独的Windows应用程序中,它为程序员、网站管理员、it管理员和几乎所有需要以更简单的方式处理远程工作的用户提供了大量的功能。 Mobaxterm为用户提供了多标签和多终端分屏选项,内置sftp文件传输以及xerver,让用户可以远程运行X窗口程序,ssh连接后会自动将远程目录展示在ssh面版中,方便用户上传下载文件。 MobaXterm提供了所有重要的远程网络工具、协议(SSH、X11、RDP、VNC、FTP、MOSH、…)和Unix命令(bash、ls、cat、sed、grep、awk、rsync、…)到Windows桌面。 MobaXterm小巧简单,仅有一个可执行文件。 官方地址: https://mobaxterm.mobatek.net/ 2. 功能介绍 1、支持各种连接SSH,X11,RDP,VNC,FTP,MOSH…… 2、内建XServer 3、连接SSH终端后支持SFTP传输文件 除了上边的一排工具,还可以拖拽上传哦。 4、友好的颜色设计 你已经看到了,MobaXterm配色看起来非常舒适,关键信息都以不同颜色区分,例如,ERROR的日志自动标记为红色,警告信息标记为黄色,等等。 5、支持多标签和多终端分屏 6、支持远程连接windows MobaXterm直接支持VNC/RDP/Xdmcp等多种远程连接方式。 7、支持多种工具 在Tool侧边栏里,有多种工具,例如文件夹比较工具MobaFoldersDiff,网络扫描器Network scanner,等等。 其他的功能不一一列举了,具体介绍和使用参见官方详细说明。 3. 下载 MobaXTerm有免费版本,但是保存的session有上限。这里提供的是专业破解版本下载,百度云有限速,下载链接如下: 链接 https://pan.baidu.com/s/1ONu6aVnV5vLFSNiUH8mDUA 密码 95gl 下载完成后解压,里边包含一个破解的MobaXterm.exe文件,执行即可。 4. 申明 1、破解软件博主亲测安全可用,不过建议下载后进行病毒查杀,博主不对软件的安全性负任何责任; 2、分享破解软件仅供个人使用,切勿用于商业用途; 3、开发一款好的产品真心不易,有条件的朋友请支持正版软件。

2018-04-02 · 1 min · 36 words · Hank

RabbitMQ基础(六)——实现RPC

第二篇中我们学习了如何在多个worker中使用工作队列来分配耗时的任务。现在,假设我们需要运行一个远程计算机上的一个方法并等待其返回结果,那么我们怎么实现?通常,这个过程被称为RPC(远程过程调用Remote Procedure Call)。 百度百科对RPC的介绍: RPC(Remote Procedure Call)--远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。 RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。 有多种 RPC模式和执行。最初由 Sun 公司提出。IETF ONC 宪章重新修订了 Sun 版本,使得 ONC RPC 协议成为 IETF 标准协议。现在使用最普遍的模式和执行是开放式软件基础的分布式计算环境(DCE)。 这里我们不过多讨论RPC相关的内容,有兴趣的可以查阅相关资料。 现在,我们将要使用RabbitMQ来构建一个RPC系统,包括可扩展的RPC客户端和服务端。由于我们并没有耗时的任务,所以们将模拟一些RPC服务。 1. Message properties Message properties即消息属性,AMQP-0-9-1预定义了14项消息属性,但是大部分都很少使用,常用的属性有如下几项: deliveryMode:标记消息是否持久化,值为2则持久化,其他为瞬态消息; contentType:描述编码的mime类型(mime-type),例如常用的JSON编码格式:application/json; replyTo:命名回调队列 correlationId:用于关联RPC的请求和响应 接下来,我们看看如何通过消息属性来设置回调队列和关联ID(correlationId)。 2. 回调队列 通常,在RabbitMQ上实现RPC很简单,客户端发送消息,而服务端响应消息即可。为了接收响应信息,客户端需要在请求中发送回调队列地址给服务端,告诉服务端我使用这个队列来接收消息,你将返回消息发送到这个队列即可。我们可以使用默认的队列(在java客户端中是独占的)。 callbackQueueName = channel.queueDeclare().getQueue(); BasicProperties props = new BasicProperties .Builder() .replyTo(callbackQueueName) .build(); channel.basicPublish("", "rpc_queue", props, message.getBytes()); 3. Correlation Id 上边的方法中,我们为每一个RPC客户端创建了回调队列,这是非常低效的。有没有更好的方式:为每一个客户端创建一个共享的回调队列?这样势必又带来新的问题:共享一个回调队列,请求和响应之间的对应关系并不明确。 CorrelationId属性解决了这个问题。 大致思路是这样:为每个请求设置一个 唯一的标识,然后通过回调队列接收响应消息时,获取这个唯一标识,如果这个标识和之前设定的相同,那么说明响应的确是当前请求的响应,可以获取请求的响应结果了。如果获取到的唯一标示并非我们所设定,说明它并不属于我们的任何请求,那么就可以丢弃这个消息。 为什么我们应该忽略回调队列中的未知消息,而不是进行失败处理?这是由于服务器端可能出现竞态条件。可能发生这样的情况,RPC服务器发送了响应消息后挂掉了,但是请求确认消息(ack)还没有发送,那么,重启后的RPC服务器将再次处理请求。这就是为什么在客户端我们必须优雅地处理重复的响应,保持RPC的幂等性。 消息定义correlationId代码: // 设置消息属性,响应后发送到回调队列中 final String correlationId = UUID.randomUUID().toString(); // 随机生成唯一标识 AMQP.BasicProperties properties = new AMQP.BasicProperties().builder() .correlationId(correlationId) // 每次请求都设定唯一标识,该标识用于将请求和响应进行匹配 .replyTo(callbackQueueName) // 回调队列 .build(); ...

2018-03-28 · 2 min · 355 words · Hank

RabbitMQ基础(五)——topic交换器

在上一篇,我们将日志系统做了改造,按照日志级别进行消息路由。我们还认识了direct类型的交换器,它是直接按照bindingKey与routingKey进行精确匹配,这两者分别在队列绑定和消息发送时进行设置。 Direct交换器每次仅能匹配一个精确的条件(bindingKey),如果要实现按照多个条件进行路由,或者按照条件进行模糊匹配,那么它就无能为力了。例如:前一篇你的日志程序,我们既要按照日志级别进行采集,还要根据打印日志的类来进行过滤,使用direct或者fanout都难以实现。 Topic交换器就是专门来处理这种场景的。在本篇,我们不再使用日志的例子,而是以动物为例,来了解topic交换器。 1. Topic交换器 发送到topic交换器的消息所设定的routingKey必须是一系列的单词列表,他们使用"."分隔。通常,这些单词会根据消息内容进行特殊定义,最大长度为255字节,举例:“stock.usd.nyse”、"syse.vmw"、"quick.range.rabbit"。 BindingKey也必须拥有相同的格式和遵循相同的规则。topic交换器和direct交换器处理逻辑上类似:带有特定routingKey的消息将被分发到绑定了匹配bindingKey的所有队列。但是,bindingKey还有两种特殊的通配符: *:能够模糊匹配一个单词 #:能够模糊匹配零个或多个单词 简单而言,topic交换器能够将消息的routingKey和队列绑定的bindingKey进行模糊匹配。如果不使用上边的两种通配符,那么topic交换器跟direct交换器没什么区别。 通配符举例: *.test.*:仅能匹配中间为test的三个单词的routingKey,例如mq.test.topic。 lazy.#:能够匹配以lazy开头的所有routingKey,单词个数不限,例如:lazy能匹配,lazy.test也能匹配 2. 示例 现在,我们来编写一个能够按照动物信息进行消息分发的程序。我们从速度、颜色和种类三个维度来描述动物信息,这里我们的key也是由这三个词语的具体描述组成,格式为“速度.颜色.种类”整体结构大致如下: 首选创建了一个类型为topic的交换器;然后我们定义了三个key,用于绑定到Q1和Q2两个队列,Q1绑定的key为*.orange.,Q2绑定的key为..rabbit和lazy.;消费者C1希望从Q1获取消息,而C2则希望从Q2获取消息。 整个程序的含义如下: 1、C1对颜色为orange(橙色)的动物感兴趣,希望获取它们的信息; 2、C2除了希望接收物种为rabbit(兔子)的消息外,还希望订阅速度为lazy(缓慢)的所有动物的信息。 很明显,C1、C2获取到的消息肯定存在重复的,它们接收消息的维度不同。 2.1. 生产者 创建交换器 // 创建交换器 channel.exchangeDeclare(exchangeName, "topic"); 模拟数据 String[] msgs = { "quick.orange.rabbit", "lazy.orange.elephant", "lazy.brown.fox", // 能匹配 "lazy.black.male.cat", // 四个单词也可以匹配 "orange", "quick.orange.male.rabbit" // 不能匹配,消息被丢弃 }; 发送消息 for (String msg : msgs) { System.out.println("发送:" + msg); channel.basicPublish(exchangeName, msg, null, msg.getBytes("utf-8")); } 为了简便,我将消息内容直接作为routingKey。实际上,routingKey需要根据消息内容进行特殊定制。 2.2. 消费者 创建随机队列 ...

2018-03-23 · 1 min · 130 words · Hank