先回顧一下,在之前的Spring Cloud Config的介紹中,我們還留了一個懸念:如何實現(xiàn)對配置信息的實時更新。雖然焙蚓,我們已經(jīng)能夠通過/refresh
接口和Git倉庫的Web Hook來實現(xiàn)Git倉庫中的內(nèi)容修改觸發(fā)應(yīng)用程序的屬性更新。但是洒宝,若所有觸發(fā)操作均需要我們手工去維護Web Hook中的應(yīng)用位置的話购公,這隨著系統(tǒng)的不斷擴張,會變的越來越難以維護雁歌,而消息代理中間件是解決該問題最為合適的方案宏浩。是否還記得我們在介紹消息代理中的特點時有提到過這樣一個功能:消息代理中間件可以將消息路由到一個或多個目的地。利用這個功能靠瞎,我們就能完美的解決該問題比庄,下面我們來說說Spring Cloud Bus中的具體實現(xiàn)方案求妹。
在《Spring Boot中使用RabbitMQ》一文中,我們已經(jīng)介紹了關(guān)于消息代理佳窑、AMQP協(xié)議以及RabbitMQ的基礎(chǔ)知識和使用方法制恍。下面我們開始具體介紹Spring Cloud Bus的配置,并以一個Spring Cloud Bus與Spring Cloud Config結(jié)合的例子來實現(xiàn)配置內(nèi)容的實時更新神凑。
RabbitMQ實現(xiàn)
下面我們來具體動手嘗試整個配置過程:
- 準備工作:這里我們不做新的應(yīng)用净神,但需要用到上一章中,我們已經(jīng)實現(xiàn)的關(guān)于Spring Cloud Config的幾個工程耙厚,若讀者對其還不了解强挫,建議先閱讀第4章的內(nèi)容岔霸。
- config-repo:定義在Git倉庫中的一個目錄薛躬,其中存儲了應(yīng)用名為didispace的多環(huán)境配置文件,配置文件中有一個from參數(shù)呆细。
- config-server-eureka:配置了Git倉庫型宝,并注冊到了Eureka的服務(wù)端。
- config-client-eureka:通過Eureka發(fā)現(xiàn)Config Server的客戶端絮爷,應(yīng)用名為didispace趴酣,用來訪問配置服務(wù)器以獲取配置信息。該應(yīng)用中提供了一個
/from
接口坑夯,它會獲取config-repo/didispace-dev.properties
中的from屬性返回岖寞。
- 擴展config-client-eureka應(yīng)用
- 修改
pom.xml
增加spring-cloud-starter-bus-amqp
模塊(注意spring-boot-starter-actuator
模塊也是必須的)。
- 修改
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
- 在配置文件中增加關(guān)于RabbitMQ的連接和用戶信息
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=springcloud
spring.rabbitmq.password=123456
- 啟動config-server-eureka柜蜈,再啟動兩個config-client-eureka(分別在不同的端口上仗谆,比如7002、7003)淑履,我們可以在config-client-eureka中的控制臺中看到如下內(nèi)容隶垮,在啟動時候,客戶端程序多了一個
/bus/refresh
請求秘噪。
o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/bus/refresh],methods=[POST]}" onto public void org.springframework.cloud.bus.endpoint.RefreshBusEndpoint.refresh(java.lang.String)
- 先訪問兩個config-client-eureka的
/from
請求狸吞,會返回當前config-repo/didispace-dev.properties
中的from屬性。 - 接著指煎,我們修改
config-repo/didispace-dev.properties
中的from屬性值蹋偏,并發(fā)送POST請求到其中的一個/bus/refresh
。 - 最后至壤,我們再分別訪問啟動的兩個config-client-eureka的
/from
請求暖侨,此時這兩個請求都會返回最新的config-repo/didispace-dev.properties
中的from屬性。
到這里崇渗,我們已經(jīng)能夠通過Spring Cloud Bus來實時更新總線上的屬性配置了字逗。
原理分析
我們通過使用Spring Cloud Bus與Spring Cloud Config的整合京郑,并以RabbitMQ作為消息代理,實現(xiàn)了應(yīng)用配置的動態(tài)更新葫掉。
整個方案的架構(gòu)如上圖所示些举,其中包含了Git倉庫、Config Server俭厚、以及微服務(wù)“Service A”的三個實例户魏,這三個實例中都引入了Spring Cloud Bus,所以他們都連接到了RabbitMQ的消息總線上挪挤。
當我們將系統(tǒng)啟動起來之后叼丑,“Service A”的三個實例會請求Config Server以獲取配置信息,Config Server根據(jù)應(yīng)用配置的規(guī)則從Git倉庫中獲取配置信息并返回扛门。
此時鸠信,若我們需要修改“Service A”的屬性。首先论寨,通過Git管理工具去倉庫中修改對應(yīng)的屬性值星立,但是這個修改并不會觸發(fā)“Service A”實例的屬性更新。我們向“Service A”的實例3發(fā)送POST請求葬凳,訪問/bus/refresh
接口绰垂。此時,“Service A”的實例3就會將刷新請求發(fā)送到消息總線中火焰,該消息事件會被“Service A”的實例1和實例2從總線中獲取到劲装,并重新從Config Server中獲取他們的配置信息,從而實現(xiàn)配置信息的動態(tài)更新昌简。
而從Git倉庫中配置的修改到發(fā)起/bus/refresh
的POST請求這一步可以通過Git倉庫的Web Hook來自動觸發(fā)占业。由于所有連接到消息總線上的應(yīng)用都會接受到更新請求,所以在Web Hook中就不需要維護所有節(jié)點內(nèi)容來進行更新江场,從而解決了通過Web Hook來逐個進行刷新的問題纺酸。
指定刷新范圍
上面的例子中,我們通過向服務(wù)實例請求Spring Cloud Bus的/bus/refresh
接口址否,從而觸發(fā)總線上其他服務(wù)實例的/refresh
餐蔬。但是有些特殊場景下(比如:灰度發(fā)布),我們希望可以刷新微服務(wù)中某個具體實例的配置佑附。
Spring Cloud Bus對這種場景也有很好的支持:/bus/refresh
接口還提供了destination
參數(shù)樊诺,用來定位具體要刷新的應(yīng)用程序。比如音同,我們可以請求/bus/refresh?destination=customers:9000
词爬,此時總線上的各應(yīng)用實例會根據(jù)destination
屬性的值來判斷是否為自己的實例名,若符合才進行配置刷新权均,若不符合就忽略該消息顿膨。
destination
參數(shù)除了可以定位具體的實例之外锅锨,還可以用來定位具體的服務(wù)。定位服務(wù)的原理是通過使用Spring的PathMatecher(路徑匹配)來實現(xiàn)恋沃,比如:/bus/refresh?destination=customers:**
必搞,該請求會觸發(fā)customers
服務(wù)的所有實例進行刷新。
架構(gòu)優(yōu)化
既然Spring Cloud Bus的/bus/refresh
接口提供了針對服務(wù)和實例進行配置更新的參數(shù)囊咏,那么我們的架構(gòu)也相應(yīng)的可以做出一些調(diào)整恕洲。在之前的架構(gòu)中,服務(wù)的配置更新需要通過向具體服務(wù)中的某個實例發(fā)送請求梅割,再觸發(fā)對整個服務(wù)集群的配置更新霜第。雖然能實現(xiàn)功能,但是這樣的結(jié)果是户辞,我們指定的應(yīng)用實例就會不同于集群中的其他應(yīng)用實例泌类,這樣會增加集群內(nèi)部的復(fù)雜度,不利于將來的運維工作咆课,比如:我們需要對服務(wù)實例進行遷移末誓,那么我們不得不修改Web Hook中的配置等扯俱。所以我們要盡可能的讓服務(wù)集群中的各個節(jié)點是對等的书蚪。
因此,我們將之前的架構(gòu)做了一些調(diào)整迅栅,如下圖所示:
我們主要做了這些改動:
- 在Config Server中也引入Spring Cloud Bus殊校,將配置服務(wù)端也加入到消息總線中來。
-
/bus/refresh
請求不在發(fā)送到具體服務(wù)實例上读存,而是發(fā)送給Config Server为流,并通過destination
參數(shù)來指定需要更新配置的服務(wù)或?qū)嵗?/li>
通過上面的改動,我們的服務(wù)實例就不需要再承擔觸發(fā)配置更新的職責让簿。同時敬察,對于Git的觸發(fā)等配置都只需要針對Config Server即可,從而簡化了集群上的一些維護工作尔当。
本文完整示例: