1、分布式配置中心解决了什么问题?
- 配置信息的外部统一管理。
- 做到系统的高可用,修改了配置文件后可在各个服务 动态刷新,不需要重启服务器。
2、架构图
3、在git上创建配置文件
这里为了演示,只创建 订单服务需要的配置
在master分支 ,项目下方 创建config目录, 下面创建一个订单 服务 dev环境的配置文件
micro-order-dev.properties :
格式注意一下, 要为 {服务名}-{profiles} ,服务名为 micro-order,配置文件环境为dev
配置内容为 :
username=wangxiaoer
4、Springcloud 配置中心服务端
4.1、新建一个Springboot配置中心子工程
4.2、导入依赖
引入配置中心服务端的依赖,
引入eureka客户端的依赖,配置中心服务端同样需要 注册到注册中心,让需要拉取配置的其他服务发现他。
org.springframework.cloud
spring-cloud-config-server
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
4.3、启动类
加上@EnableConfigServer,开启配置中心功能
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
4.4、bootstrap.properties配置
# 端口号
server.port=9002
# 服务名,其他服务 根据服务名 从配置中心服务 拉取配置
spring.application.name=config-server
# 由于配置信息要统一放到git上, 需要配置git
# 拉取配置的仓库地址
spring.cloud.config.server.git.uri=https://git.lug.ustc.edu.cn/liuben/celebrity-cloud.git
# 配置文件放在哪个目录,我们把配置文件放在了config下,这里就配置为config文件夹
spring.cloud.config.server.git.searchPaths=config
# 分支
spring.cloud.config.label=master
# 用户名和密码
spring.cloud.config.server.git.username=****
spring.cloud.config.server.git.password=****
# 注册到 注册中心
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://localhost:9001/eureka/
4.5、启动配置中心服务端 工程
通过启动日志可以看到,配置中心服务端创建了很多接口,这些接口可以对数据加密/解密 ,直接查看 配置中心服务 所拉取到的配置信息。
查看 配置信息的 接口 有很多种格式,大抵都是由 label,和 name,和profiles 这几个变量 来构成的
label : 对应代码分支
name : 对应服务名
profiles : 对应不同环境的配置文件
这里我们访问 /{label}/{name}-{profiles}.properties 这种格式的
master分支,订单工程 micro-order,配置文件环境 dev ,所以我们在浏览器这样访问:
http://localhost:9101/master/micro-order-dev.properties
可以看到,配置中心服务端服务 已经从git上拉取到了 config目录下micro-order-dev.properties的配置信息。
5、配置中心客户端配置
改造订单服务从 配置中心服务端拉取配置
5.1、引入 配置中心 客户端 依赖
org.springframework.cloud
spring-cloud-starter-config
5.2、服务内配置文件更改
将application改成 bootstrap
文件名更改,要从application.properties 改成 bootstrap.properties。
bootstrap的配置优先级更高, bootstrap文件 由父ApplicationContext加载,比application优先加载,主要用于从额外的资源来加载配置信息。
否则用application.properties,那么会从默认的8888端口去拉取配置中心服务的配置。会在移动时报以下错误:
Spring Cloud 服务启动连接 报:Fetching config from server at : http://localhost:8888
5.3、bootstrap.properties 配置 从 配置中心拉取配置
这样配置的意图 就是 从配置中心 拉取 master分支下的 micro-order-dev.properties/yml 的 配置信息,
其他分支 /其他服务 / 其他环境 /的配置不会拉取
spring.application.name=micro-order
#spring.cloud.controller.uri= http://localhost:9009/
server.port=9011
#eureka.client.service-url.defaultZone=http://localhost:9001/eureka/
eureka.client.serviceUrl.defaultZone=http://admin:admin@localhost:9001
eureka.instance.health-check-url-path=/actuator/health
# 配置 从 配置中心拉取配置信息
# 分支为master
spring.cloud.config.label=master
# 环境为dev
spring.cloud.config.profile=dev
spring.cloud.config.discovery.enabled=true
# 服务名为 配置中心服务的服务名 : config-server
spring.cloud.config.discovery.serviceId=config-server
5.4、启动
启动时,会从配置中心服务 拉取自己服务的配置
5.5、测试当前服务是否成功拉取到配置
编写一个controller:
@RequestMapping
@RestController
public class ConfigController {
@Value("${username}")
private String username;
@GetMapping("/getUsername")
public String getUsername() {
return username;
}
}
调用订单服务的getUsername接口,看 username这个配置 是否 加载成功
结果 是 成功 获取 到 了 git上配置的 username 这个 配置。
6、SpringCloudConfig客户端快速失败和重试
6.1、快速失败
当客户端连配置中心服务端失败时,客户端就快速失败,不进行加载其他的 spring 容器
bootstrap.properties配置:
#如果连接不上获取配置有问题,快速响应失败
spring.cloud.config.fail-fast=true
6.2、重试机制
1、导入重试功能依赖:
org.springframework.retry
spring-retry
2、bootstrap.properties配置:
#默认重试的间隔时间,默认 1000ms
spring.cloud.config.retry.multiplier=1000
#下一间隔时间的乘数,默认是 1.1
#spring.cloud.config.retry.initial-interval=1.1
#最大间隔时间,最大 2000ms
spring.cloud.config.retry.max-interval=2000
#最大重试次数,默认 6 次
spring.cloud.config.retry.max-attempts=6
7、配置信息的加密
在配置中心中,有些信息是比较敏感的,比如密码信息,在配置密码信息的时候有必要对密 码信息加密以免密码信息泄露,springcloud 配置中心也支持配置信息加密的,这里用 RSA 非对称加密举例。
7.1、本地生成秘钥
cd 到 jdk 的 keytool 目录:D:\Program Files\Java\jdk1.8.0_92\jre\bin 里面有一个 keytool.exe 可执行文件
7.2、执行指令生成秘钥文件
keytool -genkeypair -alias config-server -keyalg RSA -keystore config-server.keystore -validity 365
期间会让你输入密码和密钥等相关信息, 指令执行成功后会在 bin 目录生成一个 config-server.keystore 文件,把该文件 copy 到配置中 心服务工程中 resources 目录下。
7.3、配置中心服务端 配置
properties 配置文件添加秘钥配置
#加密配置
encrypt.key-store.location=config-server.keystore
encrypt.key-store.alias=config-server
# 生成config-server.keystore 设置的 密码和密钥 都是123456
encrypt.key-store.password=123456
encrypt.key-store.secret=123456
pom 中添加静态文件扫描,让能够扫描到.keystore 文件
src/main/resources
**/*.properties
**/*.keystore
false
7.4、密码加密和解密接口
在配置中心服务端中有提供对信息加密和解密接口的
7.4.1、加密接口:
http://localhost:8085/encrypt,post 请求
body 中 输入 明文 123456 加密接口 返回密文。
7.4.2、解密接口:
localhost:9101//decrypt
body中 输入 密文 ,返回解密后的 明文
说明加密解密的相关配置 是 没问题的。
7.5、代码仓库密文配置
我们对刚才git上的配置信息 配置密文
再密文前面加上 {cipher}前缀, 通知服务端解密, 后面跟着 密文
重启配置中心服务端和订单服务后,再调之前的接口
可以发现, 订单服务获取到 的 是 解密之后的明文。
8、配置动态加载刷新
这个是一个革命性的功能,在运行期修改配置文件后,我们通过这个动态刷新功能可以不重 启服务器,这样我们系统理论上可以保证 7*24 小时的工作。
8.1、Environment 的动态刷新
动态刷新其实要有一个契机,其实这个契机就是手动调用刷新接口,如果你想刷新哪台主机 的配置,就调用哪台主机的刷新接口。
# 刷新接口为:主机ip+端口 /actuator/refresh post请求
http://localhost:9011/actuator/refresh
前提是当前服务 得 引入了 服务监控包。
org.springframework.boot
spring-boot-starter-actuator
并且暴露所有接口,比如 health,info, refresh。
# 暴露 actuator包 下的所有接口
management.endpoints.web.exposure.include=*
调用接口 :
调这个接口 会通知 配置中心服务端 从 git上 拉取最新的配置, 然后 当前主机 又可以从配置中心服务端的 获取到 最新的配置, 最终 刷新系统内的 配置信息。
接口会返回 有变化的配置 的key。
8.2、@Value 注入的属性动态刷新
其实前面在调用刷新接口后,@Value 注入的属性是没有刷新的还是老的配置,这个也好理 解,@Value 注入的属性是项目启动就已经定了的。如果要使@Value 属性也刷新,就必须要 在类上面加上:@RefreshScope 注解 。
@RefreshScope 其实就是个 bean作用域的注解 , 调用接口 会使bean的作用域结束,然后这个bean重新创建注册到spring容器中,引用到 配置信息的 字段 也会从 最新的配置信息 中 赋上新值。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
// refresh作用域
@Scope("refresh")
@Documented
public @interface RefreshScope {
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
但是调用每台主机的刷新接口显然太麻烦了,如果需要刷新的集群机器有几百台,是不是就 需要手动调用几百次呢,这几乎是一个不能完成的工作量。 Springcloud 中也提供了消息总线的东西,借助 mq 来完成消息的广播,当需要刷新时我们就 只要调用一次刷新接口即可。
9、消息总线
消息总线其实很简单,就是为了解决一点刷新的功能,在一个点调用请求刷新接口,然后所 有的在消息总线中的端点都能接到刷新的消息,所有我们必须把每一个端点都拉入到消息总 线中来。
9.1、导入依赖
导入SpringcloudBus 的依赖
org.springframework.cloud
spring-cloud-starter-bus-amqp
9.2、properties 配置
这个配置 配置到 配置中心 和 自己服务下 都可以
# 其实就是连接 mq 的配置
spring.rabbitmq.host=连接rabbitmq的ip
spring.rabbitmq.port=连接rabbitmq的端口
spring.rabbitmq.username=连接rabbitmq的用户名
spring.rabbitmq.password=连接rabbitmq的密码
# 刷新配置 url http://localhost:9011/actuator/bus-refresh
spring.cloud.bus.refresh.enabled=true
spring.cloud.bus.trace.enabled=true
通过这两步就已经完成了拉入消息总线的工作了
如果要刷新配置,就只要调用任意一个消息总线端点调用刷新接口即可,其他的端点就会收 到刷新配置的消息。
刷新接口:
http://localhost:9011/actuator/bus-refresh
9.3、消息总线原理
开启了消息总线的服务,启动时会 连接rabbitmq , 创建 并消费 消息队列(自动删除,排他的 )。rabbitmq中会被注册一个统一的Topic类型的交换机。消息总线的端点创建的队列都会绑定这个交换机 ,路由键为 # ,统配所有消息格式。
当某个端点调用 /actuator/bus-refresh 接口时, 会往这个交换机发消息, 交换机将消息广播到 消息总线端点绑定的所有队列, 所有队列的 消费者(也就是所有消息总线的服务端点)消费到 消息 后 刷新 配置消息。
9.3.1、消息总线注册的交换机
9.3.2、消息总线端点 绑定的队列
9.3.3、队列与 交换机的路由键
9.4、利用github -webhook 更改配置信息 自动调用
这个接口也可以配置到 GitHub 中,只要 GitHub 有代码变更,就会调用这个接口。
到git仓库里设置 webhook
当然这里配置的 URL 是你生产环境的外网地址,这样当你 GitHub 中修改了配置后,就会主 动调用消息总线的刷新接口。
9.5、消息总线的弊端
消息总线弊端就是太重了,一个集群通知刷新配置功能,还得用一个 rabbitmq 来做,如果 项目中根本用不到 rabbitmq 呢?就加大了项目负担,加大了维护成本。