使用Spring Cloud Config咱們能實現服務配置的集中化管理,在服務啓動時從Config Server獲取須要的配置屬性。但若是在服務運行過程當中,咱們須要將某個配置屬性進行修改,好比將驗證碼的失效時間從五分鐘調整爲十分鐘,如何將這個更新在服務端不重啓服務就能動態生效,是本文討論的內容。java
Spring Cloud Bus能夠理解爲Spring Cloud體系架構中的消息總線,經過一個輕量級的Message Broker來將分佈式系統中的節點鏈接起來。可用來實現廣播狀態更新(如配置更新),或其它管理指令。
Spring Cloud Bus 就像是一個分佈式的Spring Boot Actuator, 目前提供了兩種類型的消息隊列中間件支持:RabbitMQ與Kafka(對應的pom依賴分別爲spring-cloud-starter-bus-amqp, spring-cloud-starter-bus-kafka)。git
Spring Cloud 在spring-cloud-context中添加了兩個actuator管理接口(POST請求): /actuator/env
與 /actuator/refresh
, 前者可用於更新當前服務實例Environment對象中的配置屬性,後者可用於刷新當前服務實例的配置信息。github
Spring Cloud Bus也提供了兩個對應的接口web
/actuator/bus-env
,相對於/actuator/env
, 使用鍵值對更新每一個實例的Environment,默認不暴露,需配置management.endpoints.web.exposure.include=bus-env 來開放接口訪問/actuator/bus-refresh
,相對於/actuator/refresh
,對每一個實例,清空RefreshScope
緩存,從新綁定@ConfigurationProperties, 默認不暴露,可經過配置綜上,/actuator/env
與 /actuator/refresh
是針對單個服務實例修改或刷新其配置信息,而 /actuator/bus-env
與 /actuator/bus-refresh
則是藉助於Spring Cloud Bus的消息機制做用於分佈式系統中的全部服務實例,所以前面有Spring Cloud Bus 就像是一個分佈式的Spring Boot Actuator的說法。spring
使用Spring Cloud Bus來實現服務配置動態更新的結構圖以下shell
咱們仍是之前面的springcloud-config, springcloud-eureka, springcloud-eureka-client三個項目來完成本文的案例演示。源碼地址緩存
在不引入Spring Cloud Bus的狀況下,咱們能夠經過Spring Cloud提供的actuator接口來實現單個實例的配置動態更新。服務器
依次啓動springcloud-eureka, springcloud-config, springcloud-eureka-client項目,而後修改springcloud-eureka-client的啓動端口,將8080改成8081,再啓動一個springcloud-eureka-client的服務實例。微信
springcloud-eureka-client 的測試接口代碼以下架構
@RestController @RefreshScope public class HelloController { @Autowired private Environment env; @Value("${app}") private String app; @RequestMapping("/hello") public String hello(){ return "Hello, welcome to spring cloud 2. env: " + env.getProperty("app") + ", value: " + app; } }
此時依次請求兩個實例的hello接口,獲得結果以下
咱們經過/actuator/env
接口來修改端口8080實例的屬性app的值,使用postman操做如圖
此時再請求接口返回結果以下
能夠看到Environment對象中app屬性的值已更新,可是 @Value註解的屬性值未變,可見 /actuator/env
接口只是更新了Environment對象,並不負責刷新其它方式引用的屬性值。此時請求另外一個端口爲8081的實例接口,其屬性值都未更新,也可見 /actuator/env
只做用於當前實例自己。
若是要讓8080實例的@Value屬性也動態更新,則可再調用/actuator/refresh
接口,如圖
此時再請求測試接口,獲得結果以下(@Value註解的屬性也已經更新了)
前面咱們使用 /actuator/env
與 /actuator/refresh
兩個接口能夠實現單個服務實例配置的動態更新,但在微服務架構中,服務實例可能達幾十甚至幾百個,一個個調用來作動態更新就有點太不方便了。這時就該Spring Cloud Bus登場了。
1.添加依賴與配置
在springcloud-config, 與springcloud-eureka-client兩個項目中,添加spring cloud bus的依賴與配置。
在pom.xml文件中添加依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
在application.yml配置文件中添加RabbitMQ的相關配置
spring: rabbitmq: host: 127.0.0.1 port: 5672 username: rabbitmq password: passw0rd
2.依次啓動springcloud-eureka, springcloud-config, springcloud-eureka-client項目,並以8081端口再啓動一個springcloud-eureka-client的服務實例。
3.咱們使用postman對配置服務器調用/actuator/bus-env
接口,
請求兩個服務實例的測試接口,獲得結果
兩個實例的Environment對象都已經更新,若是要將@Value註解的屬性也更新,則可再調用配置服務器的/actuator/bus-refresh
接口。
/actuator/bus-env
接口是直接更新的內存Environment實例屬性,若是服務重啓,則又還原到以前的配置了, 因此仍是須要藉助配置倉庫來永久更新。配置更新後還須要手動調用接口使其生效?DevOps時代了,能自動化的就自動化吧,咱們能夠藉助Git的webhook機制來實現自動化。
本文開頭的「使用Spring Cloud Bus來實現服務配置動態更新的結構圖」已經示例了使用Git倉庫的webhook來觸發自動更新配置的流程。可是在Git(如Github)中,咱們不能直接使用/actuator/bus-refresh
接口來做爲webhook(由於接口協議不一致,會出現解析異常),也有人經過提供本身的接口來做爲webhook,在本身接口中再轉發請求到/actuator/bus-refresh
來實現。但實際上,spring-cloud-config-monitor已經提供了對Git webhook的支持。
以下圖,spring-cloud-config-monitor提供了對Github,Gitlab,Gitee,BitBucket等的支持
1.在配置服務器springcloud-config的pom.xml文件中添加依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-monitor</artifactId> </dependency>
2.在配置倉庫的設置頁面配置webhook,好比Github的配置如圖
Payload URL 配置爲配置服務器的monitor接口地址,path參數必須。若是你的配置服務器在內網,好比作本地測試時,還須要實現一下內網穿透(如frp)。
在配置倉庫項目中修改配置屬性,提交代碼,Github webhook就會觸發自動更新,上圖下方紅色框爲觸發自動更新的記錄。
若是出現Github觸發了自動更新,但服務的配置更新未生效的狀況,則須要查看webhook的匹配規則與服務實例的ServiceID是否匹配,webhook的匹配規則爲 spring.application.name:spring.cloud.config.profile:**
,服務實例的ServiceID可經過spring.cloud.bus.id
配置,若是沒有配置,則默認爲
${vcap.application.name:${spring.application.name:application}}:${vcap.application.instance_index:${spring.application.index:${local.server.port:${server.port:0}}}}:${vcap.application.instance_id:${random.value}}
遵循app:index:id的格式,
咱們能夠在服務項目中打開spring cloud bus的debug日誌
logging: level: org.springframework.cloud.bus: debug
經過DefaultBusPathMatcher的debug日誌來查看是否匹配,如
DEBUG 286196 --- [7O8XC9KNWbyDA-1] o.s.cloud.bus.DefaultBusPathMatcher : In match: hello-service:8081:c96f04c81dfce6dffaa9d116811d127c, hello-service:8081:c96f04c81dfce6dffaa9d116811d127c
若是沒有匹配則能夠按照webhook的匹配規則設置spring.cloud.bus.id
值或vcap.application.instance_index
值,如
spring: application: name: hello-service cloud: config: discovery: service-id: config-server enabled: true profile: ${spring.profiles.active:default} bus: id: ${spring.application.name}:${spring.cloud.config.profile}:${random.value} #或 vcap: application: instance_index: ${spring.cloud.config.profile}
配置更新未生效的另外一個狀況是查看是否用了@RefreshScope註解。
細心的人會發現本文開頭的測試接口類上加了@RefreshScope註解。 @RefreshScope是Spring Cloud提供的用來實現配置、實例熱加載的註解。被@RefreshScope修飾的@Bean都是延遲加載的,即在第一次訪問(調用方法)時纔會被初始化,而且這些bean存於緩存中。當收到配置更新的消息時,緩存中的@RefreshScope bean會被清除,這樣下次訪問時將會從新建立bean,此時使用的就是最新的配置信息,從而實現配置的熱加載。
本文分別示例了使用spring boot actuator與spring cloud bus來實現服務配置的更新及二者之間的區別, spring cloud bus必定程度上像是一個分佈式的spring boot actuator。同時演示了使用webhook與spring cloud bus,monitor結合來實現配置自動更新的具體流程及可能遇到的問題。
認真生活,快樂分享!若是你以爲文章對你有幫助,歡迎分享轉發!
歡迎關注微信公衆號:空山新雨的技術空間
獲取Spring Boot,Spring Cloud,Docker等系列技術文章