前言
众所周知,在微服务领域,将会拆分出多个 子服务,每个服务的粒度都很小,并且每个服务都需要进行相关的 配置才能运行,但是如此多的子服务,每个服务都进行相关的配置,修改时也要找到对应的配置文件进行修改,将是一件非常麻烦的事情,所以SpringCloud提供了 一个ConfigServer来解决这个问题。
采用Config,来为微服务提供一个集中化的外部 配置支持,各个不同微服务应用的所有环境提供了一个中心化的 外部配置。
Config分为服务端和客户端两部分:
服务端:又叫分布式配置中心,是一个独立的微服务应用,用来连接配置服务并为客户端提供获取配置信息,加密解密信息等 访问接口。
客户端:通过指定的配置中心管理应用资源,在 启动的时候从配置中心获取和加载配置信息,配置服务器默认采用git来存储配置信息 ,这样有助于对环境配置进行版本管理,并且 可以通过git客户端工具来方便的管理和访问配置内容。
服务端
在GitHub上创建自己的config服务
新建服务端项目
1、pom文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
2、配置文件
server:
port: 3344
spring:
application:
name: cloud-config-center
cloud:
config:
server:
git:
uri: https://github.com/1742268440Zhy/springcloud-config.git #github上面的git仓库名字
# uri: git@github.com:1742268440Zhy/springcloud-config.git #github上面的git仓库名字
# 搜索目录
search-paths:
- springcloud-config
# 读取分支
label: master
注:有的童鞋可能写uri:git@github.com:1742268440Zhy/springcloud-config.git 会报错cannot clone or checkout repository,可能是因为ssl公私钥设置的 有问题,可以检查一下在电脑本地是否能通过git clone命令拉取下来代码,如果不能,就检查一下ssl的公私钥配置。如果能就可能是其他原因了,具体什么原因这里没有进行详细的解决,只是将ssl的地址改为了https的地址,这样就能通过安全https路径进行访问了。
3、主启动类
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class ConfigCenterMain3344 {
public static void main(String[] args) {
SpringApplication.run(ConfigCenterMain3344.class,args);
}
}
4、运行访问
启动服务,输入地址访问:http://localhost:3344/master/config-dev.yml
查看结果:
客户端
1、pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
2、配置文件
server:
port: 3355
spring:
application:
name: cloud-config-client
cloud:
#config客户端配置
config:
# 读取分支
label: master
name: config #配置文件名称
profile: dev # 读取后缀名称
uri: http://localhost:3344 #配置中心地址
3、启动类
@SpringBootApplication
@EnableEurekaClient
public class ConfigClientMain3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClientMain3355.class,args);
}
}
4、业务代码
@RestController
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/configInfo")
public String getConfigInfo(){
return configInfo;
}
}
5、启动 测试
启动服务端和客户端,访问地址 :http://localhost:3355/configInfo
得到的内容即从服务端拉取到的内容。
6、存在问题
此时我们在github上修改 配置文件中的内容,重新刷新服务端和客户端,看结果:
可以发现服务端因为是直接连接到git地址,所以git中的内容刷新,服务端可以直接 获取到新的内容。但是客户端监听的是 3344的服务器,而此时3344服务器更新后并没有通知到客户端。
此时我们需要手动进行相关配置,使客户端动态刷新。
修改3355客户端模块如下
pom引入actuator监控:
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
修改yml,暴露监控端口:
#暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
业务类上加@RefreshScope注解:
@RestController
@RefreshScope
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/configInfo")
public String getConfigInfo(){
return configInfo;
}
}
再次运行,此时修改git上的内容,修改完之后,需要主动发送post请求刷新3355:
curl -X POST "http://localhost:3355/actuator/refresh"
查看服务端和客户端的效果:
7、再次思考
目前我们只有一个客户端服务,如果来多个客户端服务,同时监听一个服务端,那么github上一旦有变动,就需要进行手动通知到所有的客户端,这将是一件非常麻烦的事情,所以我们在之前的基础上再次做了更新,引入SpringCloud Bus 消息总线机制。
SpringCloud Bus
引入
Bus能够实现分布式自动刷新配置功能,它结合SpringCloud config使用可以实现配置的动态刷新。SpringCloud Bus被称为消息总线机制,他能够实现将分布式系统的节点与轻量级消息系统连接起来,管理和传播系统间的消息,就像一个分布式的执行器,可以广播状态更改、事件推送等,也可以当做微服务间的通信通道,目前支持的 消息代理为RabbitMq和kafka。
他的基本原理就是,让所有的config实例都监听MQ中同一个topic(默认是SpringCloudBus)。当一个服务刷新数据的时候,他会把这个信息放入到topic中,这样其他监听同一个Topic的服务就能得到通知,然后去更新自身的配置。
基于以上原理就出现了两种设计思想:
1、利用消息总线触发一个客户端/bus/refresh,从而刷新所有客户端配置
2、利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,从而刷新所有客户端配置
我们最终选取第二种设计思想,原因如下:
1、第一种打破了微服务的职责单一性,因为微服务本身是业务模块,它本不应该承担配置刷新的职责
2、第一种设计破坏了微服务各节点的对等性
3、第一种有一定的局限性,例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新那就会增加更多的修改
动态刷新全局广播
再按照3355服务,添加一个实例服务3366.
3355和3366客户端都更改如下配置:
1、pom中引入总线amqp的依赖
<!--添加消息总线Rabbitmq支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
2、更改配置文件
配置文件中添加MQ的相关配置内容
3344服务端更改如下配置:
1、引入依赖
<!--添加消息总线Rabbitmq支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
2、更改配置文件
启动测试
先后启动3344/3355/3366三个服务,最初结果如下
此时更改github上的文件内容,并执行如下命令:
curl -X POST "http://localhost:3344/actuator/bus-refresh"
重新刷新三个服务,结果如下:
由此就达到了一次更新通知,所有监听节点能够全部更改的结果。
动态刷新定点通知
那么问题又来了,如果我只想通知一部分客户端更新,而不想通知全部客户端更新呢?
比如 只通知3355不通知3366.
很简单,在执行通知语句的时候只需要套用下面的公式即可:
公式:http://localhost:服务端端口号/actutor/bus-refresh/{destination目的端端口号}
/bus/refresh请求不再发送到具体的服务实力上,而是发给config server通过destination参数指定需要更新配置的服务或实例
比如直通知3355,则执行下面的语句:
curl -X POST "http://localhost:3344/actuator/bus-refresh/config-client:3355"