对配置中心进行优化

现在我们的配置中心使用 Spring Cloud BusSpring Cloud Config 的整合,并以 RabbitMQ 作为消息代理,实现了应用配置的动态更新。

架构如下图所示:

但是,现在的架构有个很大的缺陷,就是每次在修改配置文件后,需要手动地触发下应用的 /bus/refresh 接口,才能完成更新操作。假如我们后端有上百个不同的服务在运行的话,手动去更新简直就是灾难,更新某一个应用时,需要先查到他的 IP + 端口号。而且如果同时修改了很多服务的配置的话,一个一个去发更新请求就有些太痛苦了。

解决这个问题的办法是借助 GitLab 的 Webhook 机制,让 GitLab 帮我们去发这个请求。Webhook 用于当 GitLab 上的项目有变化的时侯以 HTTP 接口的形式通知第三方。

进入我们 GitLab 的 config-repo/app-a 仓库,在 Settings - Integrations 中可以对 Webhook 进行设置:URL 填写我们刷新配置的地址,触发器选择 Push events 就够了,然后直接保存。

现在可以测试一下,修改配置后,不需要再手动访问 /bus/refresh 也能完成更新操作了。

初步优化到这里就结束了,已经可以省去很多人力成本,简单来说就是服务的配置更新需要 GitLab 的 Webhook 通过向具体服务中的某个实例发送请求,再触发对整个服务集群的配置更新,不过这样做还是有问题的:首先一个问题是,我们在配每个服务 Webhook 的时候,其实也需要根据自己在线上不同的 IP + 端口号 来配置,另一个更严重一些的问题是,虽然我们现在的方式可以依赖消息总线,通过更新一个实例达到更新所有实例的目的,但这样做有个前提是,接受 /bus/refresh 的那个实例要保证没有宕掉,如果它挂了,配置依然不会被修改。比如我们的 app-a 服务有很多实例,我们分别取名叫 app-a-1,app-a-2,app-a-3…,现在我们在 Webhook 中设置的地址是 app-a-1 实例的 /bus/refresh 地址,假如在我们更新完 GitLab 上的配置文件后,app-a-1 那台机器刚好出了问题,这个时候其他的实例也就得不到更新了。

其实这个时候依赖哪个节点都不合适,谁也不知道哪个节点在什么时候会挂掉,可能有人会想到,我可以给所有节点都发一遍请求,我来分析一下这样做的缺点:

  • 第一点是节点很多的时候,你需要配很多 Webhook
  • 第二点是当每个实例收到消息后,都会通过 RabbitMQ 通知其他所有实例,这样做非常浪费资源而且消息总线的意义就不存在了
  • 第三点是我们指定的实例会在收到更新请求的时候立刻更新配置,并通过异步的方式来通知其他实例,这时会导致我们节点间存在不对等性,从而增加集群内部的复杂度
  • 第四如果我们需要对服务实例进行迁移,那么我们还要修改 Webhook 中的配置

所以我们需要做一些调整,让服务集群中的各个节点是对等的:我们在 Config Server 中也引入 Spring Cloud Bus,将配置服务端也加入到消息总线中来。/bus/refresh 请求不再发送到具体实例上,而是发送给 Config Server,并通过 destination 参数来指定需要更新配置的服务或实例。

Config Server 项目的 build.gradle 中加入消息总线的依赖:

compile('org.springframework.cloud:spring-cloud-starter-bus-amqp')

然后修改 application.yml,加入

1
2
3
4
5
6
7
8
9
10
11
management:
security:
enabled: false
spring:
...
rabbitmq:
host: 172.24.8.100
port: 5672
username: admin
password: admin

然后在 app-a 的 GitLab 仓库中修改我们刚才设置的 Webhook,将地址改为:http://172.24.8.100:7020/bus/refresh?destination=app-a

注意端口号已经变了,对应的是配置中心的端口,destination 的值是要刷新的服务名称,这样的话配置其他服务 Webhook 的时候,只需要修改这个名称就可以了。

通过上面的改动,我们的服务实例就不需要再承担触发配置更新的职责。同时,对于Git的触发等配置都只需要针对 Config Server 即可,从而简化了集群上的一些维护工作。

保存 Webhook 后再次修改 GitLab 上 app-a 的配置文件,提交修改后刷新页面看到结果已经变为最新的配置了。