1 配置中心为什么需要服务化
在我的前一篇博客中,客户端直接从配置中心的服务端获取配置信息。当然也不是说这种模式不行,但不觉得这种模式服务端与客户端的耦合度实在是太高了吗?这完全不符合 Spring Cloud 服务治理的理念。那我们可以换个思路,把配置中心的服务端当做一个服务注册至 Eureka 中,然后配置中心的客户端可以去 Eureka 获取服务端的服务,从而实现配置中心的服务化,同时降低配置中心服务端与客户端的耦合度。
2 配置中心服务化实战
本次我们在上一篇博客的基础上完成配置中心的服务化。
2.1 配置中心服务端改造
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置文件如下
server.port=8087
spring.application.name=config
# 设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
# git仓库的地址
spring.cloud.config.server.git.uri=https://github.com/LingHaoYuan/config
# git仓库的账号
spring.cloud.config.server.git.username=
# git仓库的密码
spring.cloud.config.server.git.password=
# 配置文件分支
spring.cloud.config.server.git.default-label=master
# 配置文件相对地址
#spring.cloud.config.server.git.search-paths=
在启动类上增加 @EnableDiscoveryClient 注解
package com.example.Config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
2.2 配置中心客户端改造
我们使用之前的 consumer 项目继续改造。
先导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
然后我们再修改一下配置文件
application.properties:
spring.application.name=consumer
server.port=8082
bootstrap.properties:
# 对应{application}部分
spring.cloud.config.name=consumer
# 对应{profile}部分
spring.cloud.config.profile=dev
# 对应git的分支
spring.cloud.config.label=master
# 开启Config服务发现支持
spring.cloud.config.discovery.enabled=true
# 指定服务端的name,也就是服务端spring.application.name的值
spring.cloud.config.discovery.serviceId=config
# 设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
同上,需要在启动类上添加 @EnableDiscoveryClient 注解。最后我们是用之前完成的控制器进行测试:
package com.example.EurekaConsumer.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ConfigTestController {
@Value("${consumer.saying}")
private String saying;
@RequestMapping("/test")
@ResponseBody
public String test() {
return saying;
}
}
2.3 测试
运行 Eureka,配置中心服务端,配置中心客户端,在浏览器输入 http://localhost:8761/
可以清楚地发现,配置中心服务端和配置中心客户端均已注册至 Eureka。
然后我们打开 http://localhost:8082/test
我们发现,配置中心客户端可以通过 Eureka 获取配置中心服务端上的配置信息,至此,我们实现了配置中心的服务化。
3 自动刷新
Spring Cloud Config 在项目启动时会加载配置内容,但这也有着一个缺陷,当我们修改配置内容的时候,它居然不会自动更新,导致我们只能在修改配置之后重启服务,才能更新配置信息。
不过,凡事无绝对,Spring Cloud Config 还是给我们留下一个刷新机制的,不过需要我们主动触发,那就是配置中心客户端使用 POST 请求来调用 refresh 这个接口,从而触发加载新配置。
但是,这样好像有点麻烦啊,我们总不能每次修改配置之后,就来调用一下 refresh 接口吧,github 给我们提供了一种 webhook 的方式,当有代码变更的时候,会调用我们设置的地址,来实现我们想达到的目的。
我们继续往下想,在只有一个客户端的时候,那我们使用 webhook ,设置手动更新啥的并不难实现。但如果客户端很多呢?一个一个手动刷新不累吗?其实在这个时候,我们可以使用 Spring Cloud Bus 的广播功能,让客户端都订阅配置更新事件,当配置更新时,触发其中一个端的更新事件,Spring Cloud Bus 就把此事件广播到其他订阅端,以此来达到批量更新。这样子多完美啊,那就这样做了!
4 Spring Cloud Bus
Spring Cloud Bus 即消息总线,将分布式系统的节点与轻量级消息代理链接。通俗的说,它可以在分布式项目中管理和传播消息,本质就是使用消息队列的广播机制在分布式的系统中传播消息,常用的消息队列包括 Kafka 和 RabbitMQ。我们可以使用 Spring Cloud Bus 的机制完成许多任务,例如,完成配置中心客户端的刷新。
我们为什么要使用 Spring Cloud Bus 去刷新配置呢?如果我们有着几十个微服务,那么当我们更改配置时,需要重启多个微服务实例。在 Spring Cloud Bus 的帮助下,我们可以令这个过程变得简单,当远程 Git 仓库的配置文件发生更改,我们只需要向某一个微服务实例发送一个 Post 请求,就可以通过消息组件通知其他微服务实例重新去仓库拉取最新的配置文件。
5 代码实战
我们继续在之前的代码上做一些简单的修改,来完成配置中心的自动刷新机制。MQ 上我们选择 RabbitMQ 来实现。
我们打算利用 Spring Cloud Bus 完成配置更新的任务,其步骤如下:首先通过 Post 请求来进行 refresh,在配置中心服务端接收请求并发送给 Spring Cloud Bus,在 Spring Cloud Bus 接收消息后会通知给其它客户端,其它客户端接收到通知之后就会请求服务端获取最新配置,从此,全部客户端均获取到最新的配置。
5.1 配置中心服务端改造
在配置中心服务端导入以下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
配置文件如下:
server.port=8087
spring.application.name=config
# 设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
# git仓库的地址
spring.cloud.config.server.git.uri=https://github.com/LingHaoYuan/config
# git仓库的账号
spring.cloud.config.server.git.username=LingHaoYuan
# git仓库的密码
spring.cloud.config.server.git.password=Ling19990607
# 配置文件分支
spring.cloud.config.server.git.default-label=master
# 配置文件相对地址
#spring.cloud.config.server.git.search-paths=
# 开启消息跟踪
spring.cloud.bus.trace.enabled=true
# 启用消息总线
spring.cloud.bus.enabled=true
management.endpoints.web.exposure.include=bus-refresh
5.2 配置中心客户端改造
导入以下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置文件 bootstrap.properties:
# 对应{application}部分
spring.cloud.config.name=consumer
# 对应{profile}部分
spring.cloud.config.profile=dev
# 对应git的分支
spring.cloud.config.label=master
# 开启Config服务发现支持
spring.cloud.config.discovery.enabled=true
# 指定服务端的name,也就是服务端spring.application.name的值
spring.cloud.config.discovery.serviceId=config
# 设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
# 开启消息跟踪
spring.cloud.bus.trace.enabled=true
# 启用消息总线
spring.cloud.bus.enabled=true
application.properties:
spring.application.name=consumer
server.port=8082
然后我们在控制器上添加 @RefreshScope 注解,如果没有这个注解的话,客户端虽然会收到服务端的更新消息,但是更新不了,因为不知道更新哪里的。
package com.example.EurekaConsumer.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RefreshScope
public class ConfigTestController {
@Value("${consumer.saying}")
private String saying;
@RequestMapping("/test")
@ResponseBody
public String test() {
return saying;
}
}
5.3 测试
我们再修改一下以前的项目 consumerCopy,把它改成与 consumer 一样。然后我们依次启动 eureka,config,consumer,consumerCopy 四个项目,先访问 http://localhost:8761/
可以清晰的看到,配置中心服务端与两个配置中心客户端都已经注册到 eureka 中。
现在我们在 Git 仓库的配置文件如下:
在浏览器输入 http://localhost:8082/test,发现其从 Git 仓库中读取配置文件成功。
然后我们修改一下 Git 仓库的配置文件,修改如下:
再次在浏览器输入 http://localhost:8082/test,http://localhost:8085/test,发现其并没有更新。(废话,我们都没有发送刷新请求)。这个时候我们需要发送一个 post 请求到 http://localhost:8087/actuator/bus-refresh 中(我们在 win 下使用命令模拟发送 post 请求),在请求发送成功后再次打开 http://localhost:8082/test,http://localhost:8085/test,发现其内容已成功被修改。可见,我们通过向8087端口的服务端发送 Post 请求刷新配置,由于使用了 Spring Cloud Bus,其他两个客户端也会接收到刷新配置的消息,并刷新配置。
参考:spring boot2.0.3+spring cloud (Finchley)6、配置中心Spring Cloud Config
springcloud(九):配置中心和消息总线(配置中心终结版)
Spring Cloud Config 实现配置中心,看这一篇就够了