上一篇Spring Cloud微服务配置中心-Config Server 中,我们实现了使用Gitlab托管配置文件集成配置中心功能,实现微服务配置统一管理。不过现实使用中,我们还需要实现配置文件更改,各个服务在不重新发布的情况下实时读取最新的配置。
实现原理
1、ConfigServer(配置中心服务端)从远端git拉取配置文件并在本地git一份,ConfigClient(微服务)从ConfigServer端获取自己对应 配置文件;
2、当远端gitlab仓库配置文件发生改变,ConfigServer如何通知到ConfigClient端,即ConfigClient如何感知到配置发生更新?
Spring Cloud Bus会向外提供一个http接口,/actuator/bus-refresh。我们将这个接口配置到远程的gitlab的webhook上,当git上的文件内容发生变动时,就会自动调用接口。Bus就会通知config-server,config-server会发布更新消息到消息总线的消息队列中,其他服务订阅到该消息就会信息刷新,从而实现整个微服务进行自动刷新。
实现方式一:
某个微服务承担配置刷新的职责
1、提交配置触发post调用客户端A的bus/refresh接口
2、客户端A接收到请求从Server端更新配置并且发送给Spring Cloud Bus总线
3、Spring Cloud bus接到消息并通知给其它连接在总线上的客户端,所有总线上的客户端均能收到消息
4、其它客户端接收到通知,请求Server端获取最新配置
5、全部客户端均获取到最新的配置
存在问题:
1、打破了微服务的职责单一性。微服务本身是业务模块,它本不应该承担配置刷新的职责。2、破坏了微服务各节点的对等性。3、有一定的局限性。WebHook的配置随着承担刷新配置的微服务节点发生改变。
改进方式二
配置中心Server端承担起配置刷新的职责,原理图如下:
1、提交配置触发post请求给server端的bus/refresh接口
2、server端接收到请求并发送给Spring Cloud Bus总线
3、Spring Cloud bus接到消息并通知给其它连接到总线的客户端
4、其它客户端接收到通知,请求Server端获取最新配置
5、全部客户端均获取到最新的配置
这里使用方式二
安装RabbitMQ
推荐使用docker的方式一键安装
docker pull rabbitmq--5672为服务访问端口 --15672为web ui访问端口docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 -v `pwd`/data:/var/lib/rabbitmq --hostname myRabbit -e RABBITMQ_DEFAULT_VHOST=my_vhost -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin ea2bf0a30abf docker exec -it c1a0bcec221e rabbitmq-plugins enable rabbitmq_management
配置中心pom引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId></dependency>
cation.yml
完整如下
客户端pom引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId></dependency>
bootstrap.yml添加rabbitmq依赖
rabbitmq: host: 10.60.25.178 port: 5672 username: admin password: admin virtual-host: my_vhost
添加注解:@RefreshScope添加在需要刷新的位置
@RestController@RefreshScopepublic class HelloController { @Value("${hello}") private String name; @RequestMapping("/tell-me-your-name") public String tellName() { return this.name; } }
先后启动server端和client端,post请求访问http://localhost:8001/actuator/bus-refresh,发现修改配置文件后,新配置可以成功同步到客户端。
使用Webhook,自动刷新
当然不能每次修改配置文件都要手动Post刷新配置,这里使用Gitlab的webhook功能,webhook配置如下:
这里敲黑板: 需要注意的是webhook要配置/monitor端点,而不是之前的/actuator/bus-refresh,否则会出现400 error,原因是调用之前的端点会引起参数反序列化异常,参数传递错误。使用monitor端点配置中心还需要添加如下依赖。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-monitor</artifactId></dependency>
配置完毕,请求正常
本以为大功告成,但是发现配置文件并没有刷新成功。纳尼?!!
查看客户端debug日志发现如下
2020-10-30 13:17:19.698 [springCloudBus.anonymous.nC62OqLTRJKv39uBbDo5mw-1] DEBUG o.springframework.cloud.bus.DefaultBusPathMatcher - matchMultiProfile : hv-administration-dev:**, hv-administration:8088:07684e380a89adc6129d633c1a4ea218
这段日志相关代码如下
1 | protected boolean matchMultiProfile(String pattern, String idToMatch) { |
发现正则与发过来的不匹配
正则--- hv-administration-dev:**发过来的 --- hv-administration:8088:07684e380a89adc6129d633c1a4ea218
查看官方文档关于spring.cloud.bus.id
的描述
可以看到每个应用都有一个Service ID, 默认的值是app:index:id
的组装,规则是:
app
:如果vcap.application.name
存在则使用vcap.application.name
,否则使用spring.application.name
(默认值为application)index
:优先使用vcap.application.instance_index
,否则依次使用spring.application.index
、local.server.port
、server.port
、0
id
:如果vcap.application.instance_id
存在则使用vcap.application.instance_id
,否则给一个随机值
从上面的日志中,我们看到是app:index:id
中的index
不一致,有如下两种修改方法
方法一:
设置vcap.application.instance_index
vcap: application: instance_index: ${spring.cloud.config.profile}
方法二:
修改bus.id匹配规则
spring: application: name: hv-administration cloud: config: uri: http://localhost:8001 profile: dev fail-fast: true bus: id: ${spring.application.name}:${spring.cloud.config.profile}:${random.value}
注意spring.application.name
和spring.cloud.config.profile
要提前定义好
这里直接使用第二种方法,客户端pom修改如下
spring: application: name: hv-administration cloud: config: uri: http://localhost:8001 profile: dev fail-fast: true bus: id: ${spring.application.name}:${spring.cloud.config.profile}:${random.value}
修改完毕,webhook测试自动请求成功200,配置文件也成功刷新!