- Java工程师的进阶之路
- 1 - SpringCloud简介与工程构建
- 2 - 服务注册中心:Eureka、Zookeeper、Consul
- 3 - 服务负载均衡:Ribbon
- 4 - 服务接口调用:OpenFeign
- 5 - 服务的断路器:Hystrix
- 6 - 服务的路由器:GateWay
- 7 - 服务配置中心:SpringCloud Config
- 8 - 服务消息驱动:SpringCloud Stream
- 9 - 服务链路跟踪:SpringCloud Sleuth
分布式配置中心
十二、Config分布式配置中心
12.1、简介
1、分布式系统面临的配置问题
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。
SpringCloud提供了ConfigServer来解决这个问题,我们每一个微服务自己带着一个application.yml,上百个配置文件的管理.……
2、是什么
SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。
3、如何实现
SpringCloud Config分为服务端和客户端两部分
- 服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口。
- 客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。
4、能干什么
- 集中管理配置文件
- 不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
- 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
- 当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
- 将配置信息以REST接口的形式暴露 - post/crul访问刷新即可…
5、与远程仓库整合
由于SpringCloud Config默认使用Git来存储配置文件(也有其它方式,比如支持SVN和本地文件),但最推荐的还是Git,而且使用的是http/https访问的形式。
12.2、服务端配置中心搭建
1、在Gitee新建空远程仓库springcloud-config
2、本地新建文件夹/cloud2022/springcloud-config
并git init
初始化仓库
3、新建三个测试配置文件
config-dev.yml
config:
info: "master branch,springcloud-config/config-dev.yml version=7"
config-prod.yml
config:
info: "master branch,springcloud-config/config-prod.yml version=1"
config-test.yml
config:
info: "master branch,springcloud-config/config-test.yml version=1"
4、新建Module模块cloud-config-center-3344
,它即为Cloud的配置中心模块CloudConfig Center
5、POM
<dependencies>
<!--添加消息总线RabbitMQ支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<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.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
6、YML
server:
port: 3344
spring:
application:
name: cloud-config-center # 注册进Eureka服务器的微服务名
cloud:
config:
server:
git:
uri: https://gitee.com/laptoy/springcloud-config.git # 远程仓库地址
search-paths:
- springcloud-config # 远程仓库目录
label: master # 读取主分支
#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
7、主启动类
@SpringBootApplication
@EnableConfigServer
public class ConfigCenterMain3344 {
public static void main(String[] args) {
SpringApplication.run(ConfigCenterMain3344.class, args);
}
}
8、windows下修改hosts文件,增加映射
127.0.0.1 config-3344.com
9、启动配置中心3344及注册中心7001
读取配置文件信息:http://config-3344.com:3344/master/config-dev.yml
12.3、配置读取规则
1、推荐该种读取规则:/{label}/{name}-{profile}.yml
- label:分支名
- name:文件名
- profile:环境名
2、演示读取master分支
http://config-3344.com:3344/master/config-dev.yml
http://config-3344.com:3344/master/config-test.yml
http://config-3344.com:3344/master/config-prod.yml
3、读取dev分支
http://config-3344.com:3344/dev/config-dev.yml
http://config-3344.com:3344/dev/config-test.yml
http://config-3344.com:3344/dev/config-prod.yml
12.4、客户端配置与测试
1、新建cloud-config-client-3355
2、POM
<dependencies>
<!--添加消息总线RabbitMQ支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<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.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3、bootstrap.yml简介
- applicaiton.yml:用户级的资源配置项
- bootstrap.yml:系统级的,优先级更高
Spring Cloud会创建一个Bootstrap Context,作为Spring应用的Application Context的父上下文。
初始化的时候,BootstrapContext负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的Environment。
Bootstrap属性有高优先级,默认情况下,它们不会被本地配置覆盖。Bootstrap context和Application Context有着不同的约定,所以新增了一个bootstrap.yml文件,保证Bootstrap Context和Application Context配置的分离。
要将Client模块下的application.yml文件改为bootstrap.yml,这是很关键的,因为bootstrap.yml是比application.yml先加载的。bootstrap.yml优先级高于application.yml。
4、新建配置文件bootstrap.yml
server:
port: 3355
spring:
application:
name: config-client
cloud:
# Config客户端配置
config:
# 读取 http://config-3344.com:3344/master/config-dev.yml
label: master # 分支名称
name: config # 配置文件名称
profile: dev # 读取后缀名称
uri: http://localhost:3344 # 配置中心地址
#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
5、主启动类
@EnableEurekaClient
@SpringBootApplication
public class ConfigClientMain3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClientMain3355.class, args);
}
}
6、控制类 - 实现从远程读取配置文件内容
@RestController
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo() {
return configInfo;
}
}
7、测试
- 启动注册中心7001、配置中心服务端3344、客户端3355
- 配置中心自测:
http://config-3344.com:3344/master/config-dev.yml
- 客户端测试:
http://localhost:3355/config/info
12.5、动态刷新手动版
分布式配置的动态刷新问题
- 运维修改了远程仓库的配置文件内容
- 刷新3344服务端可以立即响应
- 刷新3355客户端无法获取新内容
- 只能通过重启或重新加载3355才能获取新内容
解决:避免每次更新配置都要重启客户端微服务3355
步骤:修改3355客户端模块
1、优化POM
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2、优化YML
# 暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
3、优化控制类:@RefreshScope
刷新作用域
@RestController
@RefreshScope
public class ConfigClientController {
...
}
4、进行测试
- 先开启注册中心7001、再开启配置中心3344,等待3344启动完毕再启动3355客户端
- 访问
http://localhost:3355/config/info
查看内容 - 模拟运维修改远程仓库内容
- 访问
http://localhost:3355/config/info
查看内容,内容还是一样 - 模拟运维发送请求刷新3355
curl -X POST "http://localhost:3355/actuator/refresh"
- 访问
http://localhost:3355/config/info
查看内容,此时内容已修改
这种方式虽然解决每次修改都需要运维发送请求,明显太麻烦
12.6、Bus消息总线
1、简介
Spring Cloud Bus配合Spring Cloud Config使用可以实现配置的 动态刷新
Spring Cloud Bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架,它整合了Java的事件处理机制和消息中间件的功能。Spring Clud Bus目前支持RabbitMQ和Kafka。
2、什么是总线
在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个共用的消息主题,并让系统中所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称它为消息总线。在总线上的各个实例,都可以方便地广播一些需要让其他连接在该主题上的实例都知道的消息。
3、基本原理
ConfigClient实例都监听MQ中同一个topic(默认是Spring Cloud Bus)。当一个服务刷新数据的时候,它会把这个信息放入到Topic中,这样其它监听同一Topic的服务就能得到通知,然后去更新自身的配置。
4、能干嘛
Spring Cloud Bus能管理和传播分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、事件推送等,也可以当作微服务间的通信通道。
12.7、Bus之RabbitMQ环境配置
4369, 25672 (Erlang发现&集群端口)
5672, 5671 (AMQP端口)
15672 (web管理后台端口)
61613, 61614 (STOMP协议端口)
1883, 8883 (MQTT协议端口)
1、win系统安装
- 安装Erlang:下载地址
- 安装RabbitMQ:下载地址
- 打开cmd进入RabbitMQ安装目录下的sbin目录
- 启动RabbitMQ管理功能
rabbitmq-plugins enable rabbitmq _management
- 访问地址查看是否安装成功:
http://localhost:15672/
- 输入账号密码并登录:guest guest
2、Linux使用Docker安装
docker run -d --name rabbitmq \
-p 5671:5671 -p 5672:5672 -p 4369:4369 \
-p 25672:25672 -p 15671:15671 -p 15672:15672 \
rabbitmq:management
- 访问虚拟机ip+端口15672
- 输入账号密码并登录:guest guest
12.8、Bus动态刷新全局广播的设计思想和选型
12.8.1、搭建3366客户端
演示广播效果,增加复杂度,再以3355为模板再制作一个3366
1、新建cloud-config-client-3366
2、POM
<dependencies>
<!--添加消息总线RabbitMQ支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<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.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3、YML
server:
port: 3366
spring:
application:
name: config-client
cloud:
#Config客户端配置
config:
label: master #分支名称
name: config #配置文件名称
profile: dev #读取后缀名称 上述3个综合:master分支上config-dev.yml的配置文件被读取http://config-3344.com:3344/master/config-dev.yml
uri: http://localhost:3344 #配置中心地址
#服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
# 暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
4、主启动
@EnableEurekaClient
@SpringBootApplication
public class ConfigClientMain3366 {
public static void main(String[] args) {
SpringApplication.run(ConfigClientMain3366.class,args);
}
}
5、控制类
@RestController
@RefreshScope
public class ConfigClientController {
@Value("${server.port}")
private String serverPort;
@Value("${config.info}")
private String configInfo;
@GetMapping("/config/info")
public String configInfo() {
return "serverPort: "+serverPort+"\t\n\n configInfo: "+configInfo;
}
}
12.8.2、设计思想
1、利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置
2、利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置
图二的架构显然更加适合,图—不适合的原因如下:
- 打破了微服务的职责单一性,因为微服务本身是业务模块,它本不应该承担配置刷新的职责。
- 破坏了微服务各节点的对等性。
- 有一定的局限性。例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,那就会增加更多的修改。
12.8.3、Bus动态刷新全局广播配置实现
12.8.3.1、优化配置中心3344
1、给cloud-config-center-3344
配置中心服务端添加消息总线支持
2、优化POM(服务总线支持)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amap</artifactId>
</dependency>
<dependency>
<groupId>org-springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
3、YML
server:
port: 3344
spring:
application:
name: cloud-config-center #注册进Eureka服务器的微服务名
cloud:
config:
server:
git:
uri: https://gitee.com/laptoy/springcloud-config.git # 远程仓库路径
search-paths:
- springcloud-config
label: master
################rabbitmq相关配置############################
rabbitmq:
host: 120.76.55.55
port: 5672
username: guest
password: guest
###########################################################
# 服务注册中心配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
# rabbitmq相关配置,暴露bus刷新配置的端点<--------------------------
management:
endpoints: #暴露bus刷新配置的端点
web:
exposure:
include: 'bus-refresh'
12.8.3.2、优化客户端3355
1、给cloud-config-client-3355
客户端添加消息总线支持
2、POM
<!--添加消息总线RabbitNQ支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amap</artifactId>
</dependency>
<dependency>
<groupId>org-springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
3、bootstrap.yml
server:
port: 3355
spring:
application:
name: config-client
cloud:
#Config客户端配置
config:
# http://config-3344.com:3344/master/config-dev.yml
label: master # 分支名称
name: config # 配置文件名称
profile: dev # 读取后缀名称
uri: http://localhost:3344 #配置中心地址k
# rabbitmq相关配置 15672是Web管理界面的端口;5672是MQ访问的端口<----------------------
rabbitmq:
host: 120.76.55.55
port: 5672
username: guest
password: guest
# 服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
# 暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
12.8.3.3、优化客户端3366
1、给cloud-config-client-3366
客户端添加消息总线支持
2、POM
<!--添加消息总线RabbitNQ支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amap</artifactId>
</dependency>
<dependency>
<groupId>org-springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
3、bootstrap.yml
server:
port: 3366
spring:
application:
name: config-client
cloud:
#Config客户端配置
config:
# http://config-3344.com:3344/master/config-dev.yml
label: master # 分支名称
name: config # 配置文件名称
profile: dev # 读取后缀名称
uri: http://localhost:3344 # 配置中心地址
# rabbitmq相关配置 15672是Web管理界面的端口;5672是MQ访问的端口<----------------------
rabbitmq:
host: 120.76.55.55
port: 5672
username: guest
password: guest
# 服务注册到eureka地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
# 暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
12.8.3.4、测试
-
启动EurekaMain7001、ConfigcenterMain3344、ConfigclientMain3355、ConfigclicntMain3366
-
模拟运维修改远程仓库内容
-
发送刷新请求 (一次发送处处生效)
curl -X POST "http://localhost:3344/actuator/bus-refresh"
-
验证:
http://config-3344.com:3344/config-dev.yml
(成功获取新内容)http://localhost:3355/config/info
(成功获取新内容)http://localhost:3366/config/info
(成功获取新内容)
12.9、Bus动态刷新定点通知
1、指定具体某一个实例生效而不是全部
2、公式:http://localhost:3344/actuator/bus-refresh/{destination}
(服务名+端口)
3、例如只刷新3355:
curl -X POST "http://localhost:3344/actuator/bus-refresh/config-client:3355"
4、总结