spirngcloud配置中心config
概述
写在开头
微服务在 服务配置 + 服务总线
这块进行选型,共有三套方案:1.Spring Cloud Config + Spring Cloud Bus
、2.Spring Cloud Alibaba Nacos
(Nacos 官网)、3.携程 Apollo
(Github 地址:Apollo)
Spring Cloud Config
和 Spring Cloud Bus
这两哥们,倒是谈不上 停更进维
,在开发中还在使用。但是在接下来将会慢慢的被 后起之秀 Alibaba Nacos 所替代。
Nacos 可以替代 Eureka
、Spring Cloud Config
、Spring Cloud Bus
。一代三减少更多组件的使用,这样我们就可以在工作中将更多的经历放在业务逻辑上。Spring Cloud Cofig
和Spring Cloud Bus
这两个组件还是比较重要,很多公司都有在使用。
分布式系统面临的配置问题
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。
Config是什么
SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为 各个不同微服务应用 的所有环境提供了一个 中心化的外部配置 。
Config如何使用
三个角色:配置服务器,服务端,客户端
SpringCloud Config分为 服务端和客户端两部分 。
服务端也称为 分布式配置中心,它是一个独立的微服务应用 ,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口
客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。
Config主要功能
- 集中管理配置文件
- 不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
- 运行期间动态调整配置,不再需要在每个服务部署的机器上(Config客户端)编写配置文件,Config客户端会向配置中心统一拉取配置自己的信息,当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
- 将配置信息以REST接口的形式暴露,post、curl访问刷新均可
Config服务端配置与测试
由于SpringCloud Config默认使用Git来存储配置文件(也有其它方式,比如支持SVN和本地文件),但最推荐的还是Git,而且使用的是http/https访问的形式,本例中我们使用github作为我们的配置服务器。
使用步骤
-
github上新建名为microservice-cloud-config的仓库,作为配置服务器
-
提交配置文件到远程仓库
-
新建配置中心moudle,cloud-config-center3344
-
pom
新引入依赖
1 2 3 4
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
完整内容
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
<?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>cloud2020</artifactId> <groupId>org.eh</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-config-config</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.eh</groupId> <artifactId>cloud-common</artifactId> <version>${project.version}</version> </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> </project>
-
yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
server: port: 3344 spring: application: name: cloud-config-config cloud: config: server: git: uri: git@github.com:lienhui68/microservice-cloud-config.git # 搜索目录 search-paths: microservice-cloud-config # 注意github默认主分支是main,但是config默认分支是master,所以需要修改,master->main default-label: main ####读取分支 label: main
-
主启动类,添加注解@EnableConfigServer
1 2 3 4 5 6 7 8 9 10 11 12 13
package com.eh.cloud2020.config; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @EnableConfigServer @SpringBootApplication public class ConfigMain3344 { public static void main(String[] args) { SpringApplication.run(ConfigMain3344.class, args); } }
-
测试通过Config微服务是否可以从Github上获取配置内容
1 2 3 4
GET http://localhost:3344/config-dev.yml config: info: dev
配置读取规则
The HTTP service has resources in the following form:
|
|
where application
is injected as the spring.config.name
in the SpringApplication
(what is normally application
in a regular Spring Boot app), profile
is an active profile (or comma-separated list of properties), and label
is an optional git label (defaults to master
.)
- label:分支名,不写默认使用yml里配置的读取分支
- name:服务名,也就是文件名
-{profile}
前面的部分,起一个有意义的名字就可以 - profile:环境(dev/test/prod)
Spring Cloud Config Server pulls configuration for remote clients from various sources. The following example gets configuration from a git repository (which must be provided), as shown in the following example:
|
|
Other sources are any JDBC compatible database, Subversion, Hashicorp Vault, Credhub and local filesystems.
Config客户端配置与测试
bootstrap.yml
- applicaiton.yml是用户级的资源配置项
- bootstrap.yml是系统级的资源配置项, 优先级更高
Spring Cloud会创建一个Bootstrap Context
,作为Spring应用的Application Context
的 父上下文 。初始化的时候,Bootstrap Context
负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的Environment
。Bootstrap
属性有高优先级,默认情况下,它们不会被本地配置覆盖。 Bootstrap context
和Application Context
有着不同的约定, 所以新增了个bootstrap.yml
文件,保证Bootstrap Context
和Application Context
配置的分离。
客户端使用配置中心步骤
-
新建客户端moudle,cloud-config-client3355
-
pom
引入配置中心客户端依赖和web依赖(测试访问用)
1 2 3 4 5 6 7 8 9
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
完整内容:
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
<?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>cloud2020</artifactId> <groupId>org.eh</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-config-client</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.eh</groupId> <artifactId>cloud-common</artifactId> <version>${project.version}</version> </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> </project>
-
bootstrap.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
server: port: 335 spring: application: name: cloud-config-client cloud: # 下面四项拼起来就是http://localhost:3344/main/config-dev.yml config: # 分支名称 label: main # 配置文件名称 name: config # 环境profile profile: dev # 配置中心地址 uri: http://localhost:3344
-
主启动类
不需要额外加注解
1 2 3 4 5 6
@SpringBootApplication public class ConfigClientMain3355 { public static void main(String[] args) { SpringApplication.run(ConfigClientMain3355.class, args); } }
-
新建Controller类,验证是否能从配置中心读取Github上的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
package com.eh.cloud2020.config.client.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ClientController { @Value("${config.info}") private String configInfo; @GetMapping("/info") public String getConfigInfo() { return "从配置中心获取配置信息=========>" + configInfo; } }
-
测试
1 2 3 4 5 6 7 8 9 10 11 12
GET http://localhost:3355/info HTTP/1.1 200 Content-Type: text/plain;charset=UTF-8 Content-Length: 46 Date: Wed, 11 Nov 2020 11:57:37 GMT Keep-Alive: timeout=60 Connection: keep-alive 从配置中心获取配置信息=========>dev Response code: 200; Time: 25ms; Content length: 24 bytes
Config客户端之动态刷新
动态刷新也叫热刷新
问题
经过上面的配置成功从客户端访问到了github上的配置,但是问题也随之而来
分布式配置的动态刷新问题,假设运维工程师在github上修改了配置文件内容,如下
刷新3344,发现ConfigServer配置中心立刻响应
刷新3355,发现ConfigClient客户端还是保持原样,除非自己重启或者重新加载
难道每次运维修改配置文件客户端都需要重启??噩梦
解决
避免每次更新配置都要重启客户端微服务3355,可以使用@RefreshScope注解,修改客户端3355模块,具体步骤如下
-
POM引入actuator监控
1 2 3 4
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
修改YML,暴露监控endpoint
1 2 3 4 5 6
# 暴露监控endpoint management: endpoints: web: exposure: include: "*"
-
在Controller类上添加@RefreshScope注解
1 2 3
@RefreshScope @RestController public class ClientController {
-
此时修改github -> 3344变化 ->3355没有变化
-
使用post请求刷新3355
1 2
$ curl -X POST http://localhost:3355/actuator/refresh ["config.client.version","config.info"]%
-
再次刷新3355访问,ok,成功实现了客户端3355刷新,读取到最新配置内容
原理
要说清楚RefreshScope,先要了解Scope
-
Scope(org.springframework.beans.factory.config.Scope)是Spring 2.0开始就有的核心的概念
-
RefreshScope(org.springframework.cloud.context.scope.refresh)是spring cloud提供的一种特殊的scope实现,用来实现配置、实例热加载。
-
RefreshScope类结构图如下:
Scope与ApplicationContext生命周期
AbstractBeanFactory#doGetBean创建Bean实例
|
|
Singleton和Prototype是硬编码的,并不是Scope子类。 Scope实际上是自定义扩展的接口
Scope Bean实例交由Scope自己创建,例如Session对象是使用一开始注册到SessionScope的ObjectFactory创建实例的,而RefreshScope是在内建缓存中获取的。
@Scope 对象的实例化
@RefreshScope 是scopeName=“refresh"的 @Scope
|
|
@Scope 的注册 AnnotatedBeanDefinitionReader#registerBean
|
|
读取@Scope元数据, AnnotationScopeMetadataResolver#resolveScopeMetadata
|
|
Scope实例对象通过ScopedProxyFactoryBean创建,其中通过AOP使其实现ScopedObject接口,这里不再展开
现在来说说RefreshScope是如何实现配置和实例刷新的
RefreshScope注册
RefreshAutoConfiguration#RefreshScopeConfiguration
|
|
RefreshScope extends GenericScope, 大部分逻辑在 GenericScope 中
GenericScope#postProcessBeanFactory 中向AbstractBeanFactory注册自己
|
|
RefreshScope 刷新过程
入口在org.springframework.cloud.context.refresh.ContextRefresher#refresh
|
|
refreshEnvironment
|
|
refreshAll
|
|
由于设置了@ConditionalOnMissingBean(RefreshScope.class),下次就会重新从BeanFactory获取一个新的实例(该实例使用新的配置)
问题more
假设有多个微服务3355、3366、3377…,每个微服务都要执行一次post请求手动刷新?
可否广播,一次通知,处处生效?或者98%生效,留2%继续使用原有配置?
这些问题都可以通过下一篇 spirngcloud消息总线bus