技术背景
我们在上一篇讲到, Spring Boot 程序只在启动的时候加载配置文件信息, 这样在 Git 仓库配置修改之后, 虽然配置中心服务器能够读取最新的提交信息, 但是配置中心客户端却不会重新读取, 以至于不能及时的读取更新后的配置信息. 这个时候就需要一种通知刷新机制来支持了.
Refresh 机制
refresh 机制是 Spring Cloud Config 提供的一种刷新机制, 它允许客户端通过 POST 方法触发各自的 / refresh, 只要依赖 spring-boot-starter-actuator 包就拥有了 / refresh 的功能, 下面我们为我们的客户端加上刷新功能, 以支持更新配置的读取.
添加依赖
修改 spring-cloud-conifg-client, 添加监控依赖, 监控依赖包里携带了 /refresh 的功能.
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-actuator</artifactId>
- </dependency>
开启更新机制
在使用配置属性的类型加上 @RefreshScope 注解, 这样在客户端执行 /refresh 的时候就会刷新此类下面的配置属性了.
- package com.louis.spring.cloud.config.client.controller;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.cloud.context.config.annotation.RefreshScope;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.Web.bind.annotation.RestController;
- @RefreshScope
- @RestController
- class HelloController {
- @Value("${spring.config.hello}")
- private String hello;
- @RequestMapping("/hello")
- public String from() {
- return this.hello;
- }
- }
修改配置
修改配置文件添加以下内容, 开放 refresh 的相关接口.
- Bootstrap.YAML
- management:
- endpoints:
- Web:
- exposure:
- include: "*"
这样, 以后以 post 请求的方式访问 http://localhost:8552/actuator/refresh 时, 就会更新修改后的配置文件了.
特别注意:
这里存在着版本大坑, 1.x 跟 2.x 的配置不太一样, 我们用的是 2.0 + 版本, 务必注意.
1. 安全配置变更
新版本
management.endpoints.Web.exposure.include="*"
老版本
management.security.enabled=false
2. 访问地址变更
新版本
http://localhost:8552/actuator/refresh
老版本
http://localhost:8552/refresh
这里还是解释一下上面这个配置起到了什么具体作用, 其实 actuator 是一个健康检查包, 它提供了一些健康检查数据接口, refresh 功能也是其中的一个接口, 但是为了安全起见, 它默认只开放了 health 和 info 接口(启动信息会包含如下图所示信息), 而上面的配置就是设置要开放哪些接口, 我们设置成 "*", 是开放所有接口. 你也可以指定开发几个, 比如: health,info,refresh, 而这里因为我们需要用的 refresh 功能, 所以需要把 refresh 接口开放出来.
设置成 "*" 后, 启动信息会包含以下信息, 而这个叫 refresh 的 post 方法, 就是我们需要的, 上面说的接口地址变更从这里也可以看得出来.
测试效果
访问 http://localhost:8552/hello, 返回结果如下.
修改仓库配置内容, 把数字 2 改成 5, 如下图所示.
再次访问 http://localhost:8552/hello, 如我们所料, 结果并没有更新, 因为我们还没有调 refresh 方法.
通过工具或自写代码发送 post 请求 http://localhost:8552/actuator/refresh, 刷新配置.
这里通过在线测试网站发送, 地址: https://getman.cn/Mo2FX .
注意: 先让你的 Chrome 支持跨域. 设置方法: 在快捷方式的 target 后加上 --disable-Web-security --user-data-dir, 重启即可.
刷新之后, 再次访问 http://localhost:8552/hello, 返回结果如下.
查看返回结果, 刷新之后已经可以获取最新提交的配置内容, 但是每次都需要手动刷新客户端还是很麻烦, 如果客户端数量一多就简直难以忍受了, 有没有什么比较好的办法来解决这个问题呢, 那是当然的, 答案就是: Spring Cloud Bus.
Spring Cloud Bus
Spring Cloud Bus, 被大家称为消息总线, 它通过轻量级的消息代理来连接各个分布的节点, 可以利用像消息队列的广播机制在分布式系统中进行消息传播, 通过消息总线可以实现很多业务功能, 其中对于配置中心客户端刷新, 就是一个非常典型的使用场景.
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-bus-amqp</artifactId>
- </dependency>
- Bootstrap.YAML
- spring:
- application:
- name: spring-cloud-config-client
- cloud:
- consul:
- host: localhost
- port: 8500
- discovery:
- serviceName: ${spring.application.name} # 注册到 consul 的服务名称
- config:
- discovery:
- enabled: true
- serviceId: spring-cloud-config-server # 配置中心服务名称
- name: spring-config # 对应 {application} 部分
- profile: dev # 对应 {profile} 部分
- label: master # 对应 Git 的分支, 如果配置中心使用的是本地存储, 则该参数无用
- rabbitmq:
- host: localhost
- port: 5672
- username: guest
- password: guest
- management:
- endpoints:
- Web:
- exposure:
- include: "*"
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-actuator</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-bus-amqp</artifactId>
- </dependency>
- application.YAML
- server:
- port: 8551
- spring:
- application:
- name: spring-cloud-config-server
- cloud:
- consul:
- host: localhost
- port: 8500
- discovery:
- serviceName: ${spring.application.name} # 注册到 consul 的服务名称
- config:
- server:
- Git:
- uri: https://gitee.com/liuge1988/spring-cloud-demo/ # 配置 Git 仓库的地址
- search-paths: config-repository # Git 仓库地址下的相对地址, 可以配置多个, 用, 分割.
- username: username # Git 仓库的账号
- password: password # Git 仓库的密码
- rabbitmq:
- host: localhost
- port: 5672
- username: guest
- password: guest
- management:
- endpoints:
- Web:
- exposure:
- include: "*"
- org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'configServerRetryInterceptor' available
- at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:685) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
- at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1210) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
- at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
- at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
- at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.getDelegate(AnnotationAwareRetryOperationsInterceptor.java:180) ~[spring-retry-1.2.2.RELEASE.jar:na]
- at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:151) ~[spring-retry-1.2.2.RELEASE.jar:na]
- at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.8.RELEASE.jar:5.0.8.RELEASE]
- at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.0.8.RELEASE.jar:5.0.8.RELEASE]
- at org.springframework.cloud.config.client.ConfigServerInstanceProvider$$EnhancerBySpringCGLIB$$dd44720b.getConfigServerInstances(<generated>) ~[spring-cloud-config-client-2.0.0.RELEASE.jar:2.0.0.RELEASE]
- at org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration.refresh(DiscoveryClientConfigServiceBootstrapConfiguration.java:84) [spring-cloud-config-client-2.0.0.RELEASE.jar:2.0.0.RELEASE]
- at org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration.startup(DiscoveryClientConfigServiceBootstrapConfiguration.java:69) [spring-cloud-config-client-2.0.0.RELEASE.jar:2.0.0.RELEASE]
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
- at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
- package com.louis.spring.cloud.config.client;
- import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
- import org.springframework.context.annotation.Bean;
- import org.springframework.retry.interceptor.RetryInterceptorBuilder;
- import org.springframework.retry.interceptor.RetryOperationsInterceptor;
- public class RetryConfiguration {
- @Bean
- @ConditionalOnMissingBean(name = "configServerRetryInterceptor")
- public RetryOperationsInterceptor configServerRetryInterceptor() {
- return RetryInterceptorBuilder.stateless().backOffOptions(1000, 1.2, 5000).maxAttempts(10).build();
- }
- }
- spring.factories
- org.springframework.cloud.Bootstrap.BootstrapConfiguration=com.louis.spring.cloud.config.client.RetryConfiguration
来源: https://www.cnblogs.com/xifengxiaoma/p/9857110.html