javascript
Spring Cloud构建微服务架构(七)消息总线
先回顧一下,在之前的Spring Cloud Config的介紹中,我們還留了一個(gè)懸念:如何實(shí)現(xiàn)對(duì)配置信息的實(shí)時(shí)更新。雖然,我們已經(jīng)能夠通過/refresh接口和Git倉(cāng)庫(kù)的Web Hook來實(shí)現(xiàn)Git倉(cāng)庫(kù)中的內(nèi)容修改觸發(fā)應(yīng)用程序的屬性更新。但是,若所有觸發(fā)操作均需要我們手工去維護(hù)Web Hook中的應(yīng)用位置的話,這隨著系統(tǒng)的不斷擴(kuò)張,會(huì)變的越來越難以維護(hù),而消息代理中間件是解決該問題最為合適的方案。是否還記得我們?cè)诮榻B消息代理中的特點(diǎn)時(shí)有提到過這樣一個(gè)功能:消息代理中間件可以將消息路由到一個(gè)或多個(gè)目的地。利用這個(gè)功能,我們就能完美的解決該問題,下面我們來說說Spring Cloud Bus中的具體實(shí)現(xiàn)方案。
在《Spring Boot中使用RabbitMQ》一文中,我們已經(jīng)介紹了關(guān)于消息代理、AMQP協(xié)議以及RabbitMQ的基礎(chǔ)知識(shí)和使用方法。下面我們開始具體介紹Spring Cloud Bus的配置,并以一個(gè)Spring Cloud Bus與Spring Cloud Config結(jié)合的例子來實(shí)現(xiàn)配置內(nèi)容的實(shí)時(shí)更新。
RabbitMQ實(shí)現(xiàn)
下面我們來具體動(dòng)手嘗試整個(gè)配置過程:
- 準(zhǔn)備工作:這里我們不做新的應(yīng)用,但需要用到上一章中,我們已經(jīng)實(shí)現(xiàn)的關(guān)于Spring Cloud Config的幾個(gè)工程,若讀者對(duì)其還不了解,建議先閱讀第4章的內(nèi)容。
- config-repo:定義在Git倉(cāng)庫(kù)中的一個(gè)目錄,其中存儲(chǔ)了應(yīng)用名為didispace的多環(huán)境配置文件,配置文件中有一個(gè)from參數(shù)。
- config-server-eureka:配置了Git倉(cāng)庫(kù),并注冊(cè)到了Eureka的服務(wù)端。
- config-client-eureka:通過Eureka發(fā)現(xiàn)Config Server的客戶端,應(yīng)用名為didispace,用來訪問配置服務(wù)器以獲取配置信息。該應(yīng)用中提供了一個(gè)/from接口,它會(huì)獲取config-repo/didispace-dev.properties中的from屬性返回。
- 擴(kuò)展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 |
- 啟動(dòng)config-server-eureka,再啟動(dòng)兩個(gè)config-client-eureka(分別在不同的端口上,比如7002、7003),我們可以在config-client-eureka中的控制臺(tái)中看到如下內(nèi)容,在啟動(dòng)時(shí)候,客戶端程序多了一個(gè)/bus/refresh請(qǐng)求。
| 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) |
- 先訪問兩個(gè)config-client-eureka的/from請(qǐng)求,會(huì)返回當(dāng)前config-repo/didispace-dev.properties中的from屬性。
- 接著,我們修改config-repo/didispace-dev.properties中的from屬性值,并發(fā)送POST請(qǐng)求到其中的一個(gè)/bus/refresh。
- 最后,我們?cè)俜謩e訪問啟動(dòng)的兩個(gè)config-client-eureka的/from請(qǐng)求,此時(shí)這兩個(gè)請(qǐng)求都會(huì)返回最新的config-repo/didispace-dev.properties中的from屬性。
到這里,我們已經(jīng)能夠通過Spring Cloud Bus來實(shí)時(shí)更新總線上的屬性配置了。
原理分析
我們通過使用Spring Cloud Bus與Spring Cloud Config的整合,并以RabbitMQ作為消息代理,實(shí)現(xiàn)了應(yīng)用配置的動(dòng)態(tài)更新。
整個(gè)方案的架構(gòu)如上圖所示,其中包含了Git倉(cāng)庫(kù)、Config Server、以及微服務(wù)“Service A”的三個(gè)實(shí)例,這三個(gè)實(shí)例中都引入了Spring Cloud Bus,所以他們都連接到了RabbitMQ的消息總線上。
當(dāng)我們將系統(tǒng)啟動(dòng)起來之后,“Service A”的三個(gè)實(shí)例會(huì)請(qǐng)求Config Server以獲取配置信息,Config Server根據(jù)應(yīng)用配置的規(guī)則從Git倉(cāng)庫(kù)中獲取配置信息并返回。
此時(shí),若我們需要修改“Service A”的屬性。首先,通過Git管理工具去倉(cāng)庫(kù)中修改對(duì)應(yīng)的屬性值,但是這個(gè)修改并不會(huì)觸發(fā)“Service A”實(shí)例的屬性更新。我們向“Service A”的實(shí)例3發(fā)送POST請(qǐng)求,訪問/bus/refresh接口。此時(shí),“Service A”的實(shí)例3就會(huì)將刷新請(qǐng)求發(fā)送到消息總線中,該消息事件會(huì)被“Service A”的實(shí)例1和實(shí)例2從總線中獲取到,并重新從Config Server中獲取他們的配置信息,從而實(shí)現(xiàn)配置信息的動(dòng)態(tài)更新。
而從Git倉(cāng)庫(kù)中配置的修改到發(fā)起/bus/refresh的POST請(qǐng)求這一步可以通過Git倉(cāng)庫(kù)的Web Hook來自動(dòng)觸發(fā)。由于所有連接到消息總線上的應(yīng)用都會(huì)接受到更新請(qǐng)求,所以在Web Hook中就不需要維護(hù)所有節(jié)點(diǎn)內(nèi)容來進(jìn)行更新,從而解決了通過Web Hook來逐個(gè)進(jìn)行刷新的問題。
指定刷新范圍
上面的例子中,我們通過向服務(wù)實(shí)例請(qǐng)求Spring Cloud Bus的/bus/refresh接口,從而觸發(fā)總線上其他服務(wù)實(shí)例的/refresh。但是有些特殊場(chǎng)景下(比如:灰度發(fā)布),我們希望可以刷新微服務(wù)中某個(gè)具體實(shí)例的配置。
Spring Cloud Bus對(duì)這種場(chǎng)景也有很好的支持:/bus/refresh接口還提供了destination參數(shù),用來定位具體要刷新的應(yīng)用程序。比如,我們可以請(qǐng)求/bus/refresh?destination=customers:9000,此時(shí)總線上的各應(yīng)用實(shí)例會(huì)根據(jù)destination屬性的值來判斷是否為自己的實(shí)例名,若符合才進(jìn)行配置刷新,若不符合就忽略該消息。
destination參數(shù)除了可以定位具體的實(shí)例之外,還可以用來定位具體的服務(wù)。定位服務(wù)的原理是通過使用Spring的PathMatecher(路徑匹配)來實(shí)現(xiàn),比如:/bus/refresh?destination=customers:**,該請(qǐng)求會(huì)觸發(fā)customers服務(wù)的所有實(shí)例進(jìn)行刷新。
架構(gòu)優(yōu)化
既然Spring Cloud Bus的/bus/refresh接口提供了針對(duì)服務(wù)和實(shí)例進(jìn)行配置更新的參數(shù),那么我們的架構(gòu)也相應(yīng)的可以做出一些調(diào)整。在之前的架構(gòu)中,服務(wù)的配置更新需要通過向具體服務(wù)中的某個(gè)實(shí)例發(fā)送請(qǐng)求,再觸發(fā)對(duì)整個(gè)服務(wù)集群的配置更新。雖然能實(shí)現(xiàn)功能,但是這樣的結(jié)果是,我們指定的應(yīng)用實(shí)例就會(huì)不同于集群中的其他應(yīng)用實(shí)例,這樣會(huì)增加集群內(nèi)部的復(fù)雜度,不利于將來的運(yùn)維工作,比如:我們需要對(duì)服務(wù)實(shí)例進(jìn)行遷移,那么我們不得不修改Web Hook中的配置等。所以我們要盡可能的讓服務(wù)集群中的各個(gè)節(jié)點(diǎn)是對(duì)等的。
因此,我們將之前的架構(gòu)做了一些調(diào)整,如下圖所示:
我們主要做了這些改動(dòng):
通過上面的改動(dòng),我們的服務(wù)實(shí)例就不需要再承擔(dān)觸發(fā)配置更新的職責(zé)。同時(shí),對(duì)于Git的觸發(fā)等配置都只需要針對(duì)Config Server即可,從而簡(jiǎn)化了集群上的一些維護(hù)工作。
本文完整示例:
- 開源中國(guó):http://git.oschina.net/didispace/SpringCloud-Learning/tree/master/Chapter1-1-7
- GitHub:https://github.com/dyc87112/SpringCloud-Learning/tree/master/1-Brixton%E7%89%88%E6%95%99%E7%A8%8B%E7%A4%BA%E4%BE%8B/Chapter1-1-7
總結(jié)
以上是生活随笔為你收集整理的Spring Cloud构建微服务架构(七)消息总线的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android增量代码测试覆盖率工具
- 下一篇: 美团容器平台架构及容器技术实践