Spring Cloud(九):分布式配置中心和消息总线
我们在Spring Cloud(七):使用SVN存储分布式配置中心文件和实现refresh中讲到,如果需要客户端获取到最新的配置信息需要执行refresh
,我们可以利用webhook的机制每次提交代码发送请求来刷新客户端,当客户端越来越多的时候,需要每个客户端都执行一遍,这种方案就不太适合了。使用Spring Cloud Bus可以完美解决这一问题。
Spring Cloud Bus
Spring cloud bus通过轻量消息代理连接各个分布的节点。这会用在广播状态的变化(例如配置变化)或者其他的消息指令。Spring bus的一个核心思想是通过分布式的启动器对spring boot应用进行扩展,也可以用来建立一个多个应用之间的通信频道。目前唯一实现的方式是用AMQP消息代理作为通道,同样特性的设置(有些取决于通道的设置)在更多通道的文档中。
Spring cloud bus被国内很多都翻译为消息总线,也挺形象的。大家可以将它理解为管理和传播所有分布式项目中的消息既可,其实本质是利用了MQ的广播机制在分布式的系统中传播消息,目前常用的有Kafka和RabbitMQ。利用bus的机制可以做很多的事情,其中配置中心客户端刷新就是典型的应用场景之一,我们用一张图来描述bus在配置中心使用的机制。
根据此图我们可以看出利用Spring Cloud Bus做配置更新的步骤:
- 1、提交代码触发post给客户端A发送bus/refresh
- 2、客户端A接收到请求从Server端更新配置并且发送给Spring Cloud Bus
- 3、Spring Cloud bus接到消息并通知给其它客户端
- 4、其它客户端接收到通知,请求Server端获取最新配置
- 5、全部客户端均获取到最新的配置
项目示例
我们选择上一篇文章Spring Cloud(八):配置中心服务化和高可用版本的代码来改造,MQ我们使用RabbitMQ来做示例。
客户端spring-cloud-config-client改造
1、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
需要多引入spring-cloud-starter-bus-amqp
包,增加对消息总线的支持
2、配置文件
## 刷新时,关闭安全验证
management.security.enabled=false
## 开启消息跟踪
spring.cloud.bus.trace.enabled=true spring.rabbitmq.host=192.168.9.89
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=123456
配置文件需要增加RebbitMq的相关配置,这样客户端代码就改造完成了。
3、测试
依次启动spring-cloud-eureka、spring-cloud-config-server、spring-cloud-config-client项目,在启动spring-cloud-config-client项目的时候我们会发现启动日志会输出这样的一条记录。
2017-05-26 17:05:38.568 INFO 21924 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/bus/refresh],methods=[POST]}" onto public void org.springframework.cloud.bus.endpoint.RefreshBusEndpoint.refresh(java.lang.String)
说明客户端已经具备了消息总线通知的能力了,为了更好的模拟消息总线的效果,我们更改客户端spring-cloud-config-client项目的端口为8003、8004依次启动,这样测试环境就准备好了。启动后eureka后台效果图如下:
我们先分别测试一下服务端和客户端是否正确运行,访问:http://localhost:8001/neo-config/dev
,返回信息:
{
"name": "neo-config",
"profiles": [
"dev"
],
"label": null,
"version": null,
"state": null,
"propertySources": [
{
"name": "https://github.com/ityouknow/spring-cloud-starter/config-repo/neo-config-dev.properties",
"source": {
"neo.hello": "hello im dev"
}
}
]
}
说明server端都正常读取到了配置信息。
依次访问:http://localhost:8002/hello
、http://localhost:8003/hello
、http://localhost:8004/hello
,返回:hello im dev
。说明客户端都已经读取到了server端的内容。
现在我们更新neo-config-dev.properties
中neo.hello
的值为hello im dev update
并提交到代码库中,访问:http://localhost:8002/hello
依然返回hello im dev
。我们对端口为8002的客户端发送一个/bus/refresh
的post请求。在win下使用下面命令来模拟webhook.
curl -X POST http://localhost:8002/bus/refresh
执行完成后,依次访问:http://localhost:8002/hello
、http://localhost:8003/hello
、http://localhost:8004/hello
,返回:hello im dev update
。说明三个客户端均已经拿到了最新配置文件的信息,这样我们就实现了图一中的示例。
改进版本
在上面的流程中,我们已经到达了利用消息总线触发一个客户端bus/refresh
,而刷新所有客户端的配置的目的。但这种方式并不优雅。原因如下:
- 打破了微服务的职责单一性。微服务本身是业务模块,它本不应该承担配置刷新的职责。
- 破坏了微服务各节点的对等性。
- 有一定的局限性。例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,那就不得不修改WebHook的配置。
因此我们将上面的架构模式稍微改变一下
这时Spring Cloud Bus做配置更新步骤如下:
- 1、提交代码触发post请求给bus/refresh
- 2、server端接收到请求并发送给Spring Cloud Bus
- 3、Spring Cloud bus接到消息并通知给其它客户端
- 4、其它客户端接收到通知,请求Server端获取最新配置
- 5、全部客户端均获取到最新的配置
这样的话我们在server端的代码做一些改动,来支持bus/refresh
1、添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
需要多引入spring-cloud-starter-bus-amqp
包,增加对消息总线的支持
2、配置文件
server:
port: 8001
spring:
application:
name: spring-cloud-config-server
cloud:
config:
server:
git:
uri: https://github.com/ityouknow/spring-cloud-starter/ # 配置git仓库的地址
search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。
username: username # git仓库的账号
password: password # git仓库的密码
rabbitmq:
host: 192.168.0.6
port: 5672
username: admin
password: 123456 eureka:
client:
serviceUrl:
defaultZone: http://localhost:8000/eureka/ ## 注册中心eurka地址 management:
security:
enabled: false
配置文件增加RebbitMq的相关配置,关闭安全验证。这样server端代码就改造完成了。
3、测试
依次启动spring-cloud-eureka、spring-cloud-config-server、spring-cloud-config-client项目,改动spring-cloud-config-client项目端口为8003、8004依次启动。测试环境准备完成。
按照上面的测试方式,访问server端和三个客户端测试均可以正确返回信息。同样修改neo-config-dev.properties
中neo.hello
的值为hello im dev update
并提交到代码库中。在win下使用下面命令来模拟webhook触发server端bus/refresh
.
curl -X POST http://localhost:8001/bus/refresh
执行完成后,依次访问:http://localhost:8002/hello
、http://localhost:8003/hello
、http://localhost:8004/hello
,返回:hello im dev update
。说明三个客户端均已经拿到了最新配置文件的信息,这样我们就实现了上图中的示例。
其它
局部刷新
某些场景下(例如灰度发布),我们可能只想刷新部分微服务的配置,此时可通过/bus/refresh
端点的destination参数来定位要刷新的应用程序。
例如:/bus/refresh?destination=customers:8000
,这样消息总线上的微服务实例就会根据destination参数的值来判断是否需要要刷新。其中,customers:8000
指的是各个微服务的ApplicationContext ID。
destination参数也可以用来定位特定的微服务。例如:/bus/refresh?destination=customers:**
,这样就可以触发customers微服务所有实例的配置刷新。
跟踪总线事件
一些场景下,我们可能希望知道Spring Cloud Bus事件传播的细节。此时,我们可以跟踪总线事件(RemoteApplicationEvent的子类都是总线事件)。
跟踪总线事件非常简单,只需设置spring.cloud.bus.trace.enabled=true
,这样在/bus/refresh
端点被请求后,访问/trace
端点就可获得类似如下的结果:
{
"timestamp": 1495851419032,
"info": {
"signal": "spring.cloud.bus.ack",
"type": "RefreshRemoteApplicationEvent",
"id": "c4d374b7-58ea-4928-a312-31984def293b",
"origin": "stores:8002",
"destination": "*:**"
}
},
{
"timestamp": 1495851419033,
"info": {
"signal": "spring.cloud.bus.sent",
"type": "RefreshRemoteApplicationEvent",
"id": "c4d374b7-58ea-4928-a312-31984def293b",
"origin": "spring-cloud-config-client:8001",
"destination": "*:**"
}
},
{
"timestamp": 1495851422175,
"info": {
"signal": "spring.cloud.bus.ack",
"type": "RefreshRemoteApplicationEvent",
"id": "c4d374b7-58ea-4928-a312-31984def293b",
"origin": "customers:8001",
"destination": "*:**"
}
}
这个日志显示了customers:8001
发出了RefreshRemoteApplicationEvent事件,广播给所有的服务,被customers:9000
和stores:8081
接受到了。想要对接受到的消息自定义自己的处理方式的话,可以添加@EventListener
注解的AckRemoteApplicationEvent和SentApplicationEvent类型到你自己的应用中。或者到TraceRepository类中,直接处理数据。
这样,我们就可清晰地知道事件的传播细节。
/bus/refresh
BUG
/bus/refresh
有一个很严重的BUG,一直没有解决:对客户端执行/bus/refresh
之后,挂到总线的上的客户端都会从Eureka注册中心撤销登记;如果对server端执行/bus/refresh
,server端也会从Eureka注册中心撤销登记。再用白话解释一下,就是本来人家在Eureka注册中心注册的好好的,只要你对着它执行一次/bus/refresh
,立刻就会从Euraka中挂掉。
其实这个问题挺严重的,本来你利用/bus/refresh
给所有的节点来更新配置信息呢,结果把服务从Euraka中给搞掉了,那么如果别人需要调用客户端的服务的时候就直接歇菜了。不知道国内有童鞋公司在生产中用到这个功能没有,用了不就很惨烈。在网上搜索了一下,国内网友和国外网友都遇到过很多次,但是一直没有解决,很幸运就是我在写这篇文章的前几天,Netflix修复了这个问题,使用Spring Cloud最新版本的包就可以解决这个问题。由此也可以发现Spring Cloud还在快速的发展中,最新的版本可能也会有一些不稳定性,可见路漫漫而修远兮。
在pom中使用Spring Cloud的版本,解决这个bug.
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.SR1</spring-cloud.version>
</properties>
主要是这句:<spring-cloud.version>Dalston.SR1</spring-cloud.version>
Spring Cloud(九):分布式配置中心和消息总线的更多相关文章
- [转]springcloud(九):配置中心和消息总线(配置中心终结版)
https://www.cnblogs.com/ityouknow/p/6931958.html springcloud(九):配置中心和消息总线(配置中心终结版) 我们在springcloud(七) ...
- Spring Cloud Config 分布式配置中心使用教程
一.简介 在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件.在Spring Cloud中,有分布式配置中心组件spring cloud config ...
- Spring Cloud 2-Config 分布式配置中心(七)
Spring Cloud Config 1.github配置 2.服务端配置 pom.xml application.xml Application.java 3.配置和命名 1. 配置加载顺序 ...
- Spring Cloud Config 分布式配置中心【Finchley 版】
一. 介绍 1,为什么需要配置中心? 当服务部署的越来越多,规模越来越大,对应的机器数量也越来越庞大,靠人工来管理和维护服务的配置信息,变得困难,容易出错. 因此,需要一个能够动态注册和获取服务信息的 ...
- Spring Cloud (5) 分布式配置中心
Spring Cloud Config 在分布式系统中,由于服务数量很多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件.在Spring Cloud中,使用Spring Cloud ...
- spring cloud 集成分布式配置中心 apollo(单机部署apollo)
一.什么是apollo? Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用 ...
- springcloud(九):配置中心和消息总线(配置中心终结版)
我们在springcloud(七):配置中心svn示例和refresh中讲到,如果需要客户端获取到最新的配置信息需要执行refresh,我们可以利用webhook的机制每次提交代码发送请求来刷新客户端 ...
- Spring Cloud (6) 分布式配置中心-高可用
高可用 现在已经可以从配置中心读取配置文件了,当微服务很多时都从配置中心读取配置文件,这时可以将配置中心做成一个微服务,将其集群化,从而达到高可用. 改造config-server 加入eureka ...
- Spring Cloud之分布式配置中心
用服务的方式来实现 ConfigAppApplication.java package com.packtpub.ConfigApp; import org.springframework.boot. ...
随机推荐
- 2009年末最强梅麻呂3D动画游戏大作 汉化补丁
[游戏名称]:Yin荡游戯Ω(前编)-闇の眷族vs女ドラゴン- (名字请自己补) [游戏厂商]:梅麻吕3D [发售日期]:2010年04月16日 游戏评价: 梅麻呂的3D作品能够说是如今3D动画中最好 ...
- Linux进程间通信—管道
Linux下的进程通信手段基本上是从UNIX平台上的进程通信手段继承而来的.而对UNIX发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间 ...
- 使用工具将安卓Android apk应用转换成Bar BlackBerry 10格式
首先要强调的是命令行工具使用起来虽然比较方便,但是对使用者有一定要求.而使用在线的网页进行转换虽然步骤比较多,但是操作比较简单.所以,对于初步接触的同学来讲,建议还是使用上一篇博文的在线转换工具比较好 ...
- Qt 播放音频文件
Qt播放音频文件的方法有好多中,简单介绍几种 不过一下几种方式都需要在Qt工程文件中添加 QT += multimedia 第一 QMediaPlayer类 可以播放MP3文件,同时使用也 ...
- 下拉菜单select高度(兼容IE6/IE7/IE8/火狐等主流浏览器)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- SQL 服务没有及时响应启动或控制请求”的解决方法
解决方法很简单:卸载删除跟IIS相关的几个漏洞安全更新补丁即可,包括KB939373.KB2290570和KB2124261.卸载微软安全更新补丁的方法:打开控制面板的“添加删除程序”面板,勾 ...
- JAVA , TOMCAT , AXIS2 环境变量配置
想要成功配置Java的环境变量,那肯定就要安装JDK,才能开始配置的. 安装JDK 向导进行相关参数设置.如图: 正在安装程序的相关功能,如图: 选择安装的路径,可以自定义,也可以默认路径.如图: 成 ...
- python获取系统时间代码解析
import time print time.time() 输出的结果是: 1279578704.6725271 但是这样是一连串的数字不是我们想要的结果,我们可以利用time模块 ...
- 稍复杂的ionic例子:显示一个列表,并且允许点击进入列表项
这个例子,按照MVC的方式进行了分层,下面是代码: demo3.htm <!DOCTYPE html> <html ng-app="app"> <he ...
- GIS中要素的捕捉以及C++实现
这篇文章早在去年就写出来了,但是由于当时毕业论文有一段是直接引用了我的这篇文章,怕引起查重的麻烦就删掉了,在此,重新挂出来和大家一起分享. 要素的选择,也称为要素的捕捉,在CAD.计算机图形学和地理信 ...