Spring Cloud配置中心和消息总线(配置中心终结版)
前文说到如果需要客户端获取到最新的配置信息需要执行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、全部客户端均获取到最新的配置
项目示例
我们使用RabbitMQ来做示例
客户端spring-cloud-config-client改造
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
需要多引入spring-cloud-starter-bus-amqp
包,增加对消息总线的支持
配置文件
## 刷新时,关闭安全验证
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的相关配置,这样客户端代码就改造完成了。
测试
依次启动spring-cloud-eureka
、spring-cloud-config-server
、spring-cloud-config-client
项目,在启动spring-cloud-config-client
项目的时候我们会发现启动日志会输出这样的一条记录。
Mapped "{[/bus/refresh],methods=[POST]}" onto public void org.springframework.cloud.bus.endpoint.RefreshBusEndpoint.refresh(java.lang.String)
说明客户端已经具备消息总线通知的能力了,为了更好的模拟消息总线的效果,我们更改客户端spring-cloud-config-client
项目的端口为8003、8004依次启动,这样测试环境就准备好了。
我们先分别测试一下服务端和客户端是否正确运行,访问:http://localhost:8001/appblog-config/dev
,返回信息:
{
"name": "appblog-config",
"profiles": [
"dev"
],
"label": null,
"version": null,
"state": null,
"propertySources": [
{
"name": "https://github.com/iyezhou/spring-cloud-starter/config-repo/appblog-config-dev.properties",
"source": {
"appblog.hello": "Hello, I'm dev"
}
}
]
}
说明Server端正常读取到了配置信息。
依次访问:http://localhost:8002/hello
、http://localhost:8003/hello
、http://localhost:8004/hello
,返回:Hello, I'm dev
。说明客户端都已经读取到了Server端的内容。
现在我们更新appblog-config-dev.properties
中appblog.hello
的值为:Hello, I'm dev update
并提交到代码库中,访问:http://localhost:8002/hello
,依然返回Hello, I'm 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, I'm 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
添加依赖
<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>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
需要多引入spring-cloud-starter-bus-amqp
包,增加对消息总线的支持
配置文件
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端代码就改造完成了。
测试
依次启动spring-cloud-eureka
、spring-cloud-config-server
、spring-cloud-config-client
项目,改动spring-cloud-config-client
项目端口为8003、8004依次启动。测试环境准备完成。
按照上面的测试方式,访问Server端和三个客户端测试均可以正确返回信息。同样修改appblog-config-dev.properties
中appblog.hello
的值为Hello, I'm 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, I'm 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
类中,直接处理数据。
这样,我们就可清晰地知道事件的传播细节。
版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/11/spring-cloud-configuration-center-and-message-bus-configuration-center-final-version/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论