咱們在springcloud(七):配置中心svn示例和refresh中講到,若是須要客戶端獲取到最新的配置信息須要執行refresh
,咱們能夠利用webhook的機制每次提交代碼發送請求來刷新客戶端,當客戶端愈來愈多的時候,須要每一個客戶端都執行一遍,這種方案就不太適合了。使用Spring Cloud Bus能夠完美解決這一問題。javascript
Spring cloud bus經過輕量消息代理鏈接各個分佈的節點。這會用在廣播狀態的變化(例如配置變化)或者其餘的消息指令。Spring bus的一個核心思想是經過分佈式的啓動器對spring boot應用進行擴展,也能夠用來創建一個多個應用之間的通訊頻道。目前惟一實現的方式是用AMQP消息代理做爲通道,一樣特性的設置(有些取決於通道的設置)在更多通道的文檔中。html
Spring cloud bus被國內不少都翻譯爲消息總線,也挺形象的。你們能夠將它理解爲管理和傳播全部分佈式項目中的消息既可,其實本質是利用了MQ的廣播機制在分佈式的系統中傳播消息,目前經常使用的有Kafka和RabbitMQ。利用bus的機制能夠作不少的事情,其中配置中心客戶端刷新就是典型的應用場景之一,咱們用一張圖來描述bus在配置中心使用的機制。java
根據此圖咱們能夠看出利用Spring Cloud Bus作配置更新的步驟:git
咱們選擇上一篇文章springcloud(八):配置中心服務化和高可用版本的示例代碼來改造,MQ咱們使用RabbitMQ來作示例。github
客戶端spring-cloud-config-client改造web
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
須要多引入spring-cloud-starter-bus-amqp
包,增長對消息總線的支持spring
## 刷新時,關閉安全驗證 management.security.enabled=false ## 開啓消息跟蹤 spring.cloud.bus.trace.enabled=true spring.rabbitmq.host=192.168.9.89 spring.rabbitmq.port=5672 spring.rabbitmq.username=admin spring.rabbitmq.password=123456
配置文件須要增長RebbitMq的相關配置,這樣客戶端代碼就改造完成了。shell
依次啓動spring-cloud-eureka、spring-cloud-config-server、spring-cloud-config-client項目,在啓動spring-cloud-config-client項目的時候咱們會發現啓動日誌會輸出這樣的一條記錄。json
2017-05-26 17:05:38.568 INFO 21924 --- [ main] 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)
說明客戶端已經具有了消息總線通知的能力了,爲了更好的模擬消息總線的效果,咱們更改客戶端spring-cloud-config-client項目的端口爲800三、8004依次啓動,這樣測試環境就準備好了。啓動後eureka後臺效果圖以下:安全
咱們先分別測試一下服務端和客戶端是否正確運行,訪問:http://localhost:8001/neo-config/dev
,返回信息:
{
"name": "neo-config", "profiles": [ "dev" ], "label": null, "version": null, "state": null, "propertySources": [ { "name": "https://github.com/ityouknow/spring-cloud-starter/config-repo/neo-config-dev.properties", "source": { "neo.hello": "hello im dev" } } ] }
說明server端都正常讀取到了配置信息。
依次訪問:http://localhost:8002/hello
、http://localhost:8003/hello
、http://localhost:8004/hello
,返回:hello im dev
。說明客戶端都已經讀取到了server端的內容。
如今咱們更新neo-config-dev.properties
中neo.hello
的值爲hello im dev update
並提交到代碼庫中,訪問:http://localhost:8002/hello
依然返回hello im dev
。咱們對端口爲8002的客戶端發送一個/bus/refresh
的post請求。在win下使用下面命令來模擬webhook.
curl -X POST http://localhost:8002/bus/refresh
執行完成後,依次訪問:http://localhost:8002/hello
、http://localhost:8003/hello
、http://localhost:8004/hello
,返回:hello im dev update
。說明三個客戶端均已經拿到了最新配置文件的信息,這樣咱們就實現了圖一中的示例。
在上面的流程中,咱們已經到達了利用消息總線觸發一個客戶端bus/refresh
,而刷新全部客戶端的配置的目的。但這種方式並不優雅。緣由以下:
所以咱們將上面的架構模式稍微改變一下
這時Spring Cloud Bus作配置更新步驟以下:
這樣的話咱們在server端的代碼作一些改動,來支持bus/refresh
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> </dependencies>
須要多引入spring-cloud-starter-bus-amqp
包,增長對消息總線的支持
server: port: 8001 spring: application: name: spring-cloud-config-server cloud: config: server: git: uri: https://github.com/ityouknow/spring-cloud-starter/ # 配置git倉庫的地址 search-paths: config-repo # git倉庫地址下的相對地址,能夠配置多個,用,分割。 username: username # git倉庫的帳號 password: password # git倉庫的密碼 rabbitmq: host: 192.168.0.6 port: 5672 username: admin password: 123456 eureka: client: serviceUrl: defaultZone: http://localhost:8000/eureka/ ## 註冊中心eurka地址 management: security: enabled: false
配置文件增長RebbitMq的相關配置,關閉安全驗證。這樣server端代碼就改造完成了。
依次啓動spring-cloud-eureka、spring-cloud-config-server、spring-cloud-config-client項目,改動spring-cloud-config-client項目端口爲800三、8004依次啓動。測試環境準備完成。
按照上面的測試方式,訪問server端和三個客戶端測試都可以正確返回信息。一樣修改neo-config-dev.properties
中neo.hello
的值爲hello im dev update
並提交到代碼庫中。在win下使用下面命令來模擬webhook觸發server端bus/refresh
.
curl -X POST http://localhost:8001/bus/refresh
執行完成後,依次訪問:http://localhost:8002/hello
、http://localhost:8003/hello
、http://localhost:8004/hello
,返回:hello im dev update
。說明三個客戶端均已經拿到了最新配置文件的信息,這樣咱們就實現了上圖中的示例。
某些場景下(例如灰度發佈),咱們可能只想刷新部分微服務的配置,此時可經過/bus/refresh
端點的destination參數來定位要刷新的應用程序。
例如:/bus/refresh?destination=customers:8000
,這樣消息總線上的微服務實例就會根據destination參數的值來判斷是否須要要刷新。其中,customers:8000
指的是各個微服務的ApplicationContext ID。
destination參數也能夠用來定位特定的微服務。例如:/bus/refresh?destination=customers:**
,這樣就能夠觸發customers微服務全部實例的配置刷新。
一些場景下,咱們可能但願知道Spring Cloud Bus事件傳播的細節。此時,咱們能夠跟蹤總線事件(RemoteApplicationEvent的子類都是總線事件)。
跟蹤總線事件很是簡單,只需設置spring.cloud.bus.trace.enabled=true
,這樣在/bus/refresh
端點被請求後,訪問/trace
端點就可得到相似以下的結果:
{
"timestamp": 1495851419032, "info": { "signal": "spring.cloud.bus.ack", "type": "RefreshRemoteApplicationEvent", "id": "c4d374b7-58ea-4928-a312-31984def293b", "origin": "stores:8002", "destination": "*:**" } }, { "timestamp": 1495851419033, "info": { "signal": "spring.cloud.bus.sent", "type": "RefreshRemoteApplicationEvent", "id": "c4d374b7-58ea-4928-a312-31984def293b", "origin": "spring-cloud-config-client:8001", "destination": "*:**" } }, { "timestamp": 1495851422175, "info": { "signal": "spring.cloud.bus.ack", "type": "RefreshRemoteApplicationEvent", "id": "c4d374b7-58ea-4928-a312-31984def293b", "origin": "customers:8001", "destination": "*:**" } }
這個日誌顯示了customers:8001
發出了RefreshRemoteApplicationEvent事件,廣播給全部的服務,被customers:9000
和stores:8081
接受到了。想要對接受到的消息自定義本身的處理方式的話,能夠添加@EventListener
註解的AckRemoteApplicationEvent和SentApplicationEvent類型到你本身的應用中。或者到TraceRepository類中,直接處理數據。
這樣,咱們就可清晰地知道事件的傳播細節。
/bus/refresh
BUG/bus/refresh
有一個很嚴重的BUG,一直沒有解決:對客戶端執行/bus/refresh
以後,掛到總線的上的客戶端都會從Eureka註冊中心撤銷登記;若是對server端執行/bus/refresh
,server端也會從Eureka註冊中心撤銷登記。再用白話解釋一下,就是原本人家在Eureka註冊中心註冊的好好的,只要你對着它執行一次/bus/refresh
,馬上就會從Euraka中掛掉。
其實這個問題挺嚴重的,原本你利用/bus/refresh
給全部的節點來更新配置信息呢,結果把服務從Euraka中給搞掉了,那麼若是別人須要調用客戶端的服務的時候就直接歇菜了。不知道國內有童鞋公司在生產中用到這個功能沒有,用了不就很慘烈。在網上搜索了一下,國內網友和國外網友都遇到過不少次,可是一直沒有解決,很幸運就是我在寫這篇文章的前幾天,Netflix修復了這個問題,使用Spring Cloud最新版本的包就能夠解決這個問題。由此也能夠發現Spring Cloud還在快速的發展中,最新的版本可能也會有一些不穩定性,可見路漫漫而修遠兮。
在pom中使用Spring Cloud的版本,解決這個bug.
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Dalston.SR1</spring-cloud.version> </properties>
主要是這句:<spring-cloud.version>Dalston.SR1</spring-cloud.version>
,詳情能夠參考本文示例中的代碼
BUG的討論和解決過程能夠看github上面這兩個issue:
參考:
Config Server——使用Spring Cloud Bus自動刷新配置
做者:純潔的微笑
出處:http://www.ityouknow.com/版權歸做者全部,轉載請註明出處