Spring Cloud Bus介紹
總線,在微服務架構的系統中,通常會使用輕量級的消息代理來構建一個共用的消息主題,並讓系統中所有微服務實例都連接上來,由於該主題中產生的消息會被所有實例監聽和消費,所以稱他為消息總線。在總線上的各個實例,都可以方便地廣播一些需要讓他連接,在該主題上的實例都知道的消息
Spring Cloud Bus用輕量級消息代理鏈接分布式系統的節點。然后可以使用此代理來廣播狀態更改(例如配置更改)或其他管理指令。一個關鍵的想法是,該總線就像用於Spring Boot應用的分布式致動器,可以橫向擴展。但是,它也可以用作應用之間的通信渠道。
基本原理
ConfigClient實例監聽MQ中同一個topic(默認是springCloudBus)。當一個服務刷新數據的時候,它會把這個消息放入到Topic中,這樣同一Topic的服務就能得到通知,然后去更新自身的配置
Spring Cloud Bus使用
本章使用RabbitMQ,RabbitMQ知識參考:【RabbitMQ】 RabbitMQ 基本概念及測試
項目准備
使用項目,搭建參考上一章:【SpringCloud】Spring Cloud Config 客戶端(二十一)
一個Eureka注冊中心8761,一個配置中心8888,兩個配置客戶端8889、8890
其中配置中心和客戶端,都需要引入Spring Cloud Bus依賴
1 <!-- 添加消息總線RabbitMQ支持 --> 2 <dependency> 3 <groupId>org.springframework.cloud</groupId> 4 <artifactId>spring-cloud-starter-bus-amqp</artifactId> 5 </dependency>
客戶端8890與客戶端8889內容相同
客戶端pom依賴如下:

1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>test-springcloud</artifactId> 7 <groupId>com.test</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>springcloud-config-client8890</artifactId> 13 14 <dependencies> 15 16 <!-- 添加消息總線RabbitMQ支持 --> 17 <dependency> 18 <groupId>org.springframework.cloud</groupId> 19 <artifactId>spring-cloud-starter-bus-amqp</artifactId> 20 </dependency> 21 22 <!-- spring cloud config client--> 23 <dependency> 24 <groupId>org.springframework.cloud</groupId> 25 <artifactId>spring-cloud-starter-config</artifactId> 26 </dependency> 27 28 <!-- eureka client --> 29 <dependency> 30 <groupId>org.springframework.cloud</groupId> 31 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 32 </dependency> 33 34 <!-- spring boot --> 35 <dependency> 36 <groupId>org.springframework.boot</groupId> 37 <artifactId>spring-boot-starter-web</artifactId> 38 </dependency> 39 <dependency> 40 <groupId>org.springframework.boot</groupId> 41 <artifactId>spring-boot-starter-actuator</artifactId> 42 </dependency> 43 <dependency> 44 <groupId>org.springframework.boot</groupId> 45 <artifactId>spring-boot-devtools</artifactId> 46 <scope>runtime</scope> 47 <optional>true</optional> 48 </dependency> 49 <dependency> 50 <groupId>org.projectlombok</groupId> 51 <artifactId>lombok</artifactId> 52 <optional>true</optional> 53 </dependency> 54 <dependency> 55 <groupId>org.springframework.boot</groupId> 56 <artifactId>spring-boot-starter-test</artifactId> 57 <scope>test</scope> 58 </dependency> 59 60 </dependencies> 61 </project>
客戶端配置文件bootstrap.xml如下:

1 spring: 2 application: 3 name: cloud-config-client 4 cloud: 5 # Config客戶端配置 6 config: 7 # 分支名稱 8 label: master 9 # 配置文件名稱 10 name: config 11 # 讀取后綴名稱 12 profile: dev 13 # 上述3個綜合:master分支上config-dev.yml配置文件 14 # 讀取http://localhost:8888/master/config-dev.yml 15 # 配置中心地址 16 uri: http://localhost:8888
客戶端配置文件application.yml如下:

1 # 端口 2 server: 3 port: 8890 4 5 spring: 6 # RabbitMQ相關配置 7 rabbitmq: 8 host: 127.0.0.1 9 port: 5672 10 username: xxx 11 password: xxxx 12 13 eureka: 14 client: 15 service-url: 16 defaultZone: http://localhost:8761/eureka 17 18 management: 19 endpoints: 20 web: 21 exposure: 22 # 暴露刷新端點 23 include: "*"
客戶端主啟動類如下:

1 @EnableEurekaClient 2 @SpringBootApplication 3 public class ConfigClientMain8890 { 4 public static void main(String[] args) { 5 SpringApplication.run(ConfigClientMain8890.class, args); 6 } 7 }
controller如下,增加了@RefreshScope注解:

1 @RestController 2 //配置文件自動刷新 3 @RefreshScope 4 public class ConfigClientController { 5 6 @Value("${server.port}") 7 private String serverPort; 8 9 @Value("${config.info}") 10 private String configInfo; 11 12 @GetMapping("/configInfo") 13 public String getConfigInfo(){ 14 return "serverPort:" + serverPort + "\t\nconfigInfo:" + configInfo; 15 } 16 }
問題:
要刷新所有客戶端節點,難道要一個一個請求客戶端刷新?有沒有什么辦法通知所有客戶端節點刷新配置?
動態刷新全局廣播
方法一、通過配置中心節點刷新所有客戶端配置
1、啟動注冊中心Eureka,和配置中心8888,注意配置中心的配置文件需要增加配置如下,暴露刷新端點bus-refresh
1 management: 2 endpoints: 3 web: 4 exposure: 5 # 暴露刷新端點bus-refresh 6 include: "*"
2、登錄RabbitMQ的Web后台界面
查看Exchanges,配置中心默認在RabbitMQ中,創建一個名 springCloudBus 的Exchanges
springCloudBus 的類型是topic模式,用來發布訂閱
查看Queues,配置中心默認在RabbitMQ中,創建一個名 springCloudBus.anonymous.K_0ln5bVTe-zFMhh8e4Gyg 的Queues
3、在RabbitMQ的Web后台界面上,創建一個Queues,名為test.news,並且綁定 springCloudBus 這個交換器
4、啟動配置客戶端8889,同樣查看RabbitMQ的Web后台界面,發現新增了一個Queues,綁定了 springCloudBus 這個交換器
5、同理啟動配置客戶端8890,同樣查看RabbitMQ的Web后台界面,發現新增了一個Queues,綁定了 springCloudBus 這個交換器
6、訪問配置中心地址:http://localhost:8888/config-dev.yml,正常訪問獲取內容
7、分別訪問配置客戶端地址:http://localhost:8890/configInfo,http://localhost:8890/configInfo,正常訪問獲取內容同上
8、修改github倉庫配置信息,將version改為 11
9、訪問配置中心地址:http://localhost:8888/config-dev.yml,正常訪問獲取更新內容
10、分別訪問配置客戶端地址:http://localhost:8890/configInfo,http://localhost:8890/configInfo,正常訪問,內容為更新
version == 10
11、通過配置中心暴露的端點bus-refresh,請求刷新配置,使用如下命令,發送post請求
請求命令:curl -X POST http://localhost:8888/actuator/bus-refresh
12、分別訪問配置客戶端地址:http://localhost:8890/configInfo,http://localhost:8890/configInfo,正常訪問,內容為更新
version == 11,內容配置已刷新
配置更新流程分析
1、在RabbitMQ的Web后台界面上,Queues,名為test.news,you如下
1 {"type":"RefreshRemoteApplicationEvent","timestamp":1588005088488,"originService":"cloud-config-conter:8888:832ba2cb48de978a1a2f18cafceaa487","destinationService":"**","id":"39b2a14a-d33f-467b-afce-d8f6df114480"} 2 3 {"type":"AckRemoteApplicationEvent","timestamp":1588005088510,"originService":"cloud-config-conter:8888:832ba2cb48de978a1a2f18cafceaa487","destinationService":"**","id":"2c978df5-16d1-470f-89f9-3b7e6d6c8761","ackId":"39b2a14a-d33f-467b-afce-d8f6df114480","ackDestinationService":"**","event":"org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent"} 4 5 {"type":"AckRemoteApplicationEvent","timestamp":1588005095313,"originService":"cloud-config-client:8889:445a5639525ab336961c83b89ca99e5d","destinationService":"**","id":"bf42cd5a-c8f8-4f22-bff2-0dbb93622606","ackId":"39b2a14a-d33f-467b-afce-d8f6df114480","ackDestinationService":"**","event":"org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent"} 6 7 {"type":"AckRemoteApplicationEvent","timestamp":1588005094238,"originService":"cloud-config-client:8890:3dff8a206f30a8e4ab9236eb915b8471","destinationService":"**","id":"9cbf695b-bbd9-4867-94f6-e983feae6d14","ackId":"39b2a14a-d33f-467b-afce-d8f6df114480","ackDestinationService":"**","event":"org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent"}
事件介紹
Bus 中定義了遠程事件 RemoteApplicationEvent
,該事件繼承了 Spring 的事件 ApplicationEvent
,而且它目前有 4 個具體的實現:
-
- EnvironmentChangeRemoteApplicationEvent: 遠程環境變更事件。 主要用於接收一個
Map<String, String>
類型的數據並更新到 Spring 上下文中Environment
中的事件。文中的實例就是使用這個事件並配合EnvironmentBusEndpoint
和EnvironmentChangeListener
完成的。 - AckRemoteApplicationEvent: 遠程確認事件。 Bus 內部成功接收到遠程事件后會發送回
AckRemoteApplicationEvent
確認事件進行確認。 - RefreshRemoteApplicationEvent: 遠程配置刷新事件。 配合
@RefreshScope
以及所有的@ConfigurationProperties
注解修飾的配置類的動態刷新。 - UnknownRemoteApplicationEvent:遠程未知事件。 Bus 內部消息體進行轉換遠程事件的時候如果發生異常會統一包裝成該事件。
- EnvironmentChangeRemoteApplicationEvent: 遠程環境變更事件。 主要用於接收一個
2、分析,配置刷新過程先由配置中心,發送一個刷新配置事件的消息,一個配置中心和兩個配置客戶端收到刷新配置事件的消息后,進行邏輯處理,然后分別發送遠程確認事件消息進行確認
方法二、通過配置中心節點刷新所有客戶端配置
1、同上可以在一個配置中心客戶端暴露端點bus-refresh
2、通過配置客戶端暴露的端點bus-refresh,請求刷新配置,使用如下命令,發送post請求
請求命令:curl -X POST http://localhost:8889/actuator/bus-refresh
3、查看各個客戶端配置文件,發現配置內容已更新
注意:不推薦此方案,在微服務中配置客戶端往往是業務服務,節點可能經常變動
而且不能即做業務功能,又做配置功能,應該保持服務功能的單一性,推薦使用方法一處理更新配置
動態刷新定點通知
1、方法一、通過配置中心節點刷新所有客戶端配置的基礎上
2、通過執行命令:curl -X POST http://localhost:8888/actuator/bus-refresh/cloud-config-client:8890
其中:cloud-config-client為應用名稱
8890為某一節點端口
3、訪問8890配置客戶端,發現配置已更新