目录
前言
分布式系统面临的----配置问题
微服务 意味着要将单个应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态配置管理设施是必不可少的。
SpringCloud提供了ConfigServer来解决这个问题,每个微服务都带着一个application.yml,上百个配置文件的管理比较麻烦。
一、SpringCloud Config是什么
SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置.
1.1 SpringCloud Config分为服务端和客户端两部分
- 服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接服务器并为客户端提供获取配置信息,加密/解密信息等访问接口
- 客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息,配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。
SpringCloud Config的作用 - 集中管理配置文件
- 不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
- 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
- 当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
- 将配置信息以REST接口的形式暴露, post、curl访问刷新均可。
- 与GitHub整合配置,由于SpringCloud Config默认使用Git来存储配置文件(也有其他方式,比如SVN和本地文件),但最推荐的还是Git,而且使用的是http/https访问的形式
1.2 Config服务端配置与测试
用自己的账号在github上新建一个名为springcloud-config
git clone https://github.com/diligentkong/springcloud-config.git
添加三个文件,具体如下:
新建module ,cloud-config-center-3344
pom文件
<dependencies>
<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>
application.yml文件
server:
port: 3344
spring:
application:
name: cloud-config-center #注册进Eureka服务器的服务名
cloud:
config:
server:
git:
uri: https://github.com/diligentkong/springcloud-config.git # github上的git仓库名字
search-paths:
- springcloud-config #搜索目录
label: master #读取分支
eureka:
client:
#表示是否将自己注册进EurekaServer中 默认为true
register-with-eureka: true
#是否从Eureka Server抓取已有的注册信息,默认为true 单节点无所谓,集群必须设置为true 才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
主启动类
@SpringBootApplication
@EnableConfigServer
public class ConfigCenterMain3344 {
public static void main(String[] args) {
SpringApplication.run(ConfigCenterMain3344.class,args);
}
}
Windows下修改hosts文件,增加映射
127.0.0.1 config-3344.com
测试: 启动微服务3344
访问http://config-3344.com:3344/master/config-dev.yml
读取配置规则: /{label}/{name}-{profile}.yml
lable:分支 name:服务名 profiles:环境{dev/test/prod}
1.3 config客户端配置与测试
新建cloud-config-client-3355
pom文件
<dependencies>
<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>
bootstarp.yml
server:
port: 3355
spring:
application:
name: config-client
cloud:
#config客户端配置
config:
label: master #分支名称
name: config #配置文件名称
profile: dev #读取后缀名称 上述3个综合,master分支上config-dev.yml配置文件被读取 http://localhost:3344/master/config-dev.yml
uri: http://localhost:3344 #配置中心地址
eureka:
client:
#表示是否将自己注册进EurekaServer中 默认为true
register-with-eureka: true
#是否从Eureka Server抓取已有的注册信息,默认为true 单节点无所谓,集群必须设置为true 才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
application.yml 是用户级的资源配置项
bootstrap.yml是系统级的,优先级更高
Spring Cloud会创建一个“BootStrap Context” ,作为Spring应用的“Application Context”的父上下文。初始化的时候,“BootStrap Context”负责从 外部资源 加载配置属性并解析配置。这两个上下文共享一个从外部获取的“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
主启动类:
@SpringBootApplication
@EnableEurekaClient
public class ConfigClientMain3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClientMain3355.class,args);
}
}
业务类:
@RestController
public class ConfigClientController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/configInfo")
public String getConfigInfo(){
return configInfo;
}
}
测试:
启动config配置中心3344微服务自测,
访问: http://config-3344.com:3344/master/config-dev.yml
结果:
config:
info: master branch,springcloud-config/config-dev.yml version=1
访问: http://config-3344.com:3344/master/config-prod.yml
结果:
config:
info: master branch,springcloud-config/config-prod.yml version=1
启动3355作为Client准备访问
http://localhost:3355/configInfo
结果:
master branch,springcloud-config/config-dev.yml version=1
成功实现了客户端3355访问springcloud config3344通过GitHub获取配置信息
问题随之而来,分布式配置的动态刷新问题:
运维修改GitHub上的配置文件内容做调整,刷新3344,发现ConfigServer配置中心立刻响应
刷新3355,发现ConfigClient客户端没有任何响应
3355没有变化除非重启或者重新加载,难道每次运维修改配置文件,客户端都需要重启?可怕!
解决办法:Config客户端之动态刷新
避免每次更新配置都要重启客户端微服务3355
修改3355模块,修改yml文件
#暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
ConfigClientController中加上注解@RefreshScope
启动7001,3344 3355
再修改github
再访问http://localhost:3355/configInfo
发现没有发生变化
打开cmd 输入curl -X POST “http://localhost:3355/actuator/refresh”
再次访问http://localhost:3355/configInfo
成功实现了客户端3355刷新到最新配置内容
问题来了:加入有多个微服务客户端,每个微服务都要执行一次post请求,手动刷新,工作量太大,能不能实现,一次通知,处处生效。
解决办法:消息总线 SpringCloud Bus
二、SpringCloud Bus
2.1 简介
SpringCloud Bus配合SpringCloud Config使用可以实现配置的动态刷新
pring Cloud Bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架,它整合了Java的事件处理机制和消息中间件的功能
Spring Cloud Bus目前支持RabbitMQ和Kafka.
作用: Spring Cloud Bus能管理和传播分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、事件推送等,也可以当做微服务间的通信通道。
- 什么是总线
在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个共用的消息主题,并让系统中所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称它为消息总线。 - 基本原理:
ConfigClient实例都监听MQ中同一个topic(默认是springCloudBus)。当一个服务刷新数据的时候,它会把这个信息放入到Topic中,这样其它监听同一Topic的服务就能得到通知,然后去更新自身的配置。
2.2 RabbitMQ环境配置
- 安装Erlang,下载
Erlang下载地址 http://erlang.org/download/
安装完成之后,运行cmd窗口,输入命令
rabbitmq-plugins enable rabbitmq_management
这样就可以添加可视化插件了
这样就添加了rabbitmq界面,只要启动rabbitmq,然后再浏览器中输入http://localhost:15672/
等等
执行:rabbitmq-plugins enable rabbitmq_management 发现web控制台的插件不可用
http://localhost:15672/ 不能登录:
解决方法:
-
文件夹为隐藏 需要再文件夹选项中把隐藏文件夹打开显示
C:\Users\Administrator\AppData\Roaming\RabbitMQ\db 里面的数据删除 再次安装一下rabbitmq-server-3.8.0.exe -
然后执行rabbitmq-plugins enable rabbitmq_management
就可以访问到了
http://localhost:15672/
账号guest 密码guest
具备了良好的RabbitMQ环境之后,开始我们的代码演示,
2.3 新建module cloud-config-client-3366
以3355为模板,新建一个3366,新建module cloud-config-client-3366
设计思想:
-
利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置
-
利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置
图二的架构显然更加适合,图一不适合的原因如下: -
打破了微服务的职责单一性,因为微服务本身是业务模块,它本不应该承担配置刷新的职责
-
破坏了微服务各节点的对等性
-
有一定局限性,例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,就会增加更过的修改。
给cloud-config-center-3344配置中心服务端添加消息总线支持,给cloud-config-client-3366客户端添加消息总线支持
pom文件
<!--添加消息总线RabbitMQ支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
yml文件
#rabbitmq相关配置
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
给cloud-config-client-3366客户端添加消息总线支持
测试:启动7001 3344 3355 3366
修改github上的配置文件
发送post请求
curl -X POST "http://localhost:3344/actuator/bus-refresh"
访问:
http://config-3344.com:3344/master/config-dev.yml
http://localhost:3355/configInfo
http://localhost:3366/configInfo
一次修改,广播通知,处处生效
2.4 SpringCloud Bus 动态刷新定点通知
不想全部通知,只想定点通知 ,只通知3355 不通知3366
指定具体某一个实例生效而不是全部
公式:http://localhost:端口号/actuator/bus-refresh/{destination}"
/bus/refresh请求不再发送到具体的服务实例上,而是发送给config server并通过destination参数类指定需要更新配置的服务或实例
以刷新运行在3355端口上的config-client为例,只通知3355 不通知3366
修改github上config-dev.yml
发送post请求
curl -X POST “http://localhost:3344/actuator/bus-refresh/config-client:3355”
访问:http://config-3344.com:3344/master/config-dev.yml
http://localhost:3355/configInfo
http://localhost:3366/configInfo
结果是3355发生了改变 3366不变
github源代码地址:https://github.com/diligentkong/cloud2020