Config
SpringCloud Config 分布式配置中心
概述
微服务意味着要将单应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。
springCloud提供了ConfigServer来解决这个问题,我们每一个微服务自己带着一个application.yml,上百个配置文件的管理。比如数据库的信息,我们可以写到一个统一的地方。
- config+bus
- alibaba nacos
- 携程 阿波罗
SpringCloud Config是什么:Spring Cloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。
怎么玩:
SpringCloud Config分为服务端和客户端两部分。
- 服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口
- 客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心取和加载配置信息配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容
能干嘛:
- 集中管理配置文件
- 不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
- 运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务会向配置中心统一拉取配置自己的信息
- 当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
- 将配置信息以REST接囗的形式暴露
config服务端配置
这个服务端指的是消费端与github之间的桥接
首先在github上新建一个仓库 springcloud-config
git@github.com:名字/项目.git
然后使用git命令克隆到本地,命令:git clone https://github.com/LZXYF/springcloud-config
注意上面的操作不是必须的,只要github上有就可以,克隆到本地只是修改文件。
常用命令:
- git add
- git commit -m “标记”
- git push origin master
在git根目录下创建
- 开发环境:config-dev.yml
- 生产环境:config-pro.yml
- 测试环境:config-test.tml
注意格式
新建 config-center3344
模块:
pom文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud2022</artifactId> <groupId>com.wzg.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>config-center3344</artifactId>
<dependencies> <!-- config Server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
<!--eureka-client config Server也要注册进服务中心--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</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> <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity --> <groupId>com.wzg.springcloud</groupId> <artifactId>api-commons</artifactId> <version>${project.version}</version> </dependency> </dependencies>
<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties>
</project>
|
yml 配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| server: port: 3344
spring: application: name: config-center cloud: config: server: git: uri: https://github.com/yztldxdza/springcloud-config.git search-paths: - springcloud-config label: master
eureka: client: service-url: defaultZone: http://localhost:7001/eureka
|
主启动类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.wzg.springcloud;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication @EnableConfigServer public class ConfigCenterMain3344 { public static void main(String[] args) { SpringApplication.run(ConfigCenterMain3344.class,args); } }
|
添加模拟映射:【C:\Windows\System32\drivers\etc\hosts】文件中添加: 127.0.0.1 config-3344.com
启动微服务3344,访问 http://config-3344.com:3344/master/config-dev.yml 文件(注意,要提前在git上弄一个这文件)
文件命名和访问的规则:
不加分支名默认是master:
最后一个出的是json串
- label:分支branch
- name:服务名
- profiles:环境dev/test/prod
config客户端配置
这里的客户端指的是,使用 Config Server 统一配置文件的项目。既有之前说的消费者,又有提供者
新建 config-client-3355
模块:
pom文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud2022</artifactId> <groupId>com.wzg.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>config-client-3355</artifactId>
<dependencies> <!-- config Client 和 服务端的依赖不一样 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
<!--eureka-client config Server也要注册进服务中心--> <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> <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity --> <groupId>com.wzg.springcloud</groupId> <artifactId>api-commons</artifactId> <version>${project.version}</version> </dependency> </dependencies>
<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties>
</project>
|
要将Client模块下的application.yml文件改为bootstrap.yml,删掉application.yml这是很关键的
因为bootstrap.yml是比application.yml先加载的。bootstrap.yml优先级高于application.yml
appllication.yml是用户级的资源配置项
bootstrap.ym1是系统级的,优先级更加高
SpringCloud会创建一个”Bootstrap Context”作为Spring应用的ApplicationContext的父上下文
。初始化的时候,Bootstrap Context负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的Environment
Bootstrap 属性有高优先级,默认情况下,它们不会被本地配置覆盖。BootstrapContext和ApplicationContext、有着不同的约定,所以新增了一个bootstrap.yml文件,保证BootstrapContext和ApplicationContext配置的分离。
bootstrap.yml文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| server: port: 3355
spring: application: name: config-client cloud: config: label: master name: config profile: dev uri: http://localhost:3344
eureka: client: service-url: defaultZone: http://localhost:7001/eureka
management: endpoints: web: exposure: include: "*"
|
主启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.wzg.springcloud;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication @EnableEurekaClient public class ConfigClientMain3355{ public static void main(String[] args) { SpringApplication.run(ConfigClientMain3355.class, args); } }
|
controller层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.wzg.springcloud.controller;
import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RefreshScope public class ConfigClientController {
@Value("${config.info}") private String configInfo;
@GetMapping("/configInfo") public String getConfigInfo(){ return configInfo; } }
|
启动测试完成!如果报错,注意github上的 yml 格式有没有写错!
启动Config配置中心3344微服务并自测,启动3355作为client访问 localhost:3355/configInfo
修改config-dev.yml配置文件并提交到github中,比如加个变量age或者版本号version。更改消费者端的配置看看其他环境能不能用
动态刷新
问题:
- Linux运维修改GitHub上的配置文件内容做调整:比如修改config-dev.yml提交
- 刷新3344,发现ConfigServer服务端配置中心立刻响应,得到最新值了
- 刷新3355,发现ConfigClient客户端没有任何响应,拿到的还是旧值
- 客户端3355没有变化除非自己重启或者重新加载,才能拿到最新值
- 难到每次运维修改配置文件,客户端都需要重启??噩梦
就是github上面配置更新了,config Server 项目上是动态更新的,但是,client端的项目中的配置,目前还是之前的,它不能动态更新,必须重启才行。
动态刷新问题解决:
client端一定要有actuator依赖:
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
|
client 端增加 yml 配置如下,即在 bootstrap.yml 文件中:
1 2 3 4 5 6
| management: endpoints: web: exposure: include: "*"
|
3.在controller 上添加注解@RefreshScope
:
到此为止,配置已经完成,但是测试客户端 localhost:3355/configInfo 仍然不能动态刷新,还是旧值(也就是说环境变量里的还是旧值),需要下一步。
4.向 client 端发送一个 POST 请求
如 curl -X POST “http://localhost:3355/actuator/refresh”
两个必须:1.必须是 POST 请求,2.请求地址:http://localhost:3355/actuator/refresh
成功获得到最新值
但是又有一个问题,就是要向每个微服务客户端发送一次POST请求,当微服务数量庞大,又是一个新的问题。
能否广播,一次通知,处处生效?(还要求不要全广播,差异化管理,定点清除,20台只有18台更新)
就有下面的消息总线!
Bus
spring cloud Bus配置spring cloud Config使用可以实现配置的动态刷新
spring cloud bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架,它整合了java的事件处理机制和消息中间件的功能。
spring cloud bus目前支持RabbitMQ和Kafka(因为是主题订阅)
什么是总线
在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个共用的消息主题,并让系统中所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称它为消息总线。在总线上的各个实例,都可以方便地广播一些需要让貝他连接在该主题上的实例都知道的消息。
基本原理:
ConfigClient
实例都监听MQ中同一个topic主题(默认是springCloud Bus
)。当一个服务刷新数据的时候,它会把这个信息放入到Topic中,这样其它监听同一topic的服务就能得到通知,然后去更新自身的配置
Bus能管理和传输分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、事件推送等,也可以当做微服务间的通信通道
RabbitMQ
在windows 上安装RabbitMQ
- 安装RabbitMQ的依赖环境 Erlang 下载地址: http://erlang.org/download/otp_win64_21.3.exe
- 安装RabbitMQ 下载地址: http://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe
- 进入 rabbitMQ安装目录的sbin目录下,打开cmd窗口,执行 【
rabbitmq-plugins enable rabbitmq_management
】
- 访问【http://localhost:15672/】,输入密码和账号:默认为guest
广播式刷新配置
必须先具有良好的RabbitMQ环境
演示广播效果,增加复杂度,再以3355为模板再制作一个3366
设计思想
给cloud-config-center-3344配置中心服务端添加消息总线支持
给cloud-config-client-3355客户端添加消息总线支持
给cloud-config-client-3366客户端添加消息总线支持(以3355为模板)
测试
一次修改,广播通知,出处生效
但还是得发一个POST请求,只不过只给config发而已
首先给 config Server 和 config client 都添加如下依赖:
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
|
config Server 的yml文件增加如下rabbitmq配置:
1 2 3 4 5 6 7 8 9 10 11 12
| rabbitmq: host: localhost port: 5672 username: guest password: guest
management: endpoints: web: exposure: include: 'bus-refresh'
|
config Client 的yml文件修改成如下配置:(注意对齐方式,和config Server不一样)
1 2 3 4 5 6 7 8
| spring: cloud: config: label: master name: client-config profile: test uri: http://config-3344.com:3344
|
3355客户端的bootstrap.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| server: port: 3355
spring: application: name: config-client cloud: config: label: master name: config profile: dev uri: http://localhost:3344
rabbitmq: host: localhost port: 5672 username: guest password: guest
eureka: client: service-url: defaultZone: http://localhost:7001/eureka
management: endpoints: web: exposure: include: "*"
|
3344注册中心的application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| server: port: 3344
spring: application: name: cloud-config-center cloud: config: server: git: uri: git@github.com:zzyybs/springcloud-config.git search-paths: - springcloud-config label: master
rabbitmq: host: localhost port: 5672 username: guest password: guest
eureka: client: service-url: defaultZone: http://localhost:7001/eureka
management: endpoints: web: exposure: include: 'bus-refresh'
|
可在github上修改yml文件进行测试,修改完文件,向 config server 发送 请求:
给3344发就能全局同步了【curl -X POST “http://localhost:3344/actuator/bus-refresh”】
注意,之前是向config client 一个个发送请求,但是这次是向 config Server 发送请求,而所有的config client 的配置也都全部更新。
定点通知
新的需求:指定具体某一个实例(的参数)生效而不是全部,一些是最新值,一些是旧值
- 公式:
http://localhost:配置中心的端口号/actuator/bus-refresh/{destination}
- 例子:curl -X POST “http//localhost:3344/actuator/bus-refresh/config-client:3355
- 即微服务名称+端囗号
- /bus/refresh请求不再发送到具体的服务实例上,而是发给configserver并通过destination参数类指定需要更新配置的服务或实例
- 我们这里以刷新运行在3355端口上的config-client为例