Spring Cloud Bus
Spring Cloud Bus用輕量級的訊息代理將分散式系統的節點連線起來。這可以用來廣播狀態的該表(比如配置的改變)或者其他關聯的指令。一個關鍵的想法是,匯流排就像是一個分散式Actuator,用於Spring Boot應用程式的擴充套件,但它也可以用作應用程式之間的通訊通道。Spring Cloud提供了AMQP 傳輸的代理和Kafka啟動Starters,對具有相同的基本功能集的其他傳輸元件的支援,也在未來的規劃中。
Spring Cloud Bus
是在Spring Cloud Stream
的基礎上進行的封裝,對於指定主題的訊息的釋出與訂閱是通過Spring Cloud Stream
的具體binder實現。因此引入的依賴可以是spring-cloud-starter-bus-amqp
和spring-cloud-starter-bus-kafka
其中的一種,分別對應於binder的兩種實現。根據上一節的基礎應用,我們總結出Spring Cloud Bus
的主要功能如下兩點:
- 對指定主題
springCloudBus
的訊息訂閱與釋出。 - 事件監聽,包括重新整理事件、環境變更事件、遠端應用的ack事件以及本地服務端傳送事件等。
下面我們以這兩方面作為主線,進行Spring Cloud Bus
的原始碼分析。本文主要針對事件的訂閱戶釋出。
事件的訂閱與釋出
事件驅動模型
這部分需要讀者首先了解下Spring的事件驅動模型。我們在這邊簡單介紹下設計的主要概念,幫助大家易於理解後面的內容。
Spring的事件驅動模型由三部分組成:
- 事件:ApplicationEvent,繼承自JDK的EventObject,所有事件將繼承它,並通過source得到事件源。
- 事件釋出者:ApplicationEventPublisher及ApplicationEventMulticaster介面,使用這個介面,我們的Service就擁有了釋出事件的能力。
- 事件訂閱者:ApplicationListener,繼承自JDK的EventListener,所有監聽器將繼承它。
事件的定義
Spring的事件驅動模型的事件定義均繼承自ApplicationEvent
,Spring Cloud Bus
中有多個事件類,這些事件類都繼承了一個重要的抽象類RemoteApplicationEvent
,我們看一下事件類的類圖:
涉及的事件類有:代表了對特定事件確認的事件AckRemoteApplicationEvent
、環境變更的事件EnvironmentChangeRemoteApplicationEvent
、重新整理事件RefreshRemoteApplicationEvent
、傳送事件 SentApplicationEvent
、以及未知事件UnknownRemoteApplicationEvent
。下面我們分別看一下這些事件的定義。
抽象基類:RemoteApplicationEvent
通過上面的類圖,我們知道RemoteApplicationEvent
是其他事件類的基類,定義了事件物件的公共屬性。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") //序列化時使用子類的名稱作為type
@JsonIgnoreProperties("source") //序列化時,忽略 source
public abstract class RemoteApplicationEvent extends ApplicationEvent {
private static final Object TRANSIENT_SOURCE = new Object();
private final String originService;
private final String destinationService;
private final String id;
protected RemoteApplicationEvent(Object source, String originService,
String destinationService) {
super(source);
this.originService = originService;
if (destinationService == null) {
destinationService = "**";
}
if (!"**".equals(destinationService)) {
if (StringUtils.countOccurrencesOf(destinationService, ":") <= 1
&& !StringUtils.endsWithIgnoreCase(destinationService, ":**")) {
//destination的所有例項
destinationService = destinationService + ":**";
}
}
this.destinationService = destinationService;
this.id = UUID.randomUUID().toString();
}
...
}
複製程式碼
在RemoteApplicationEvent
中定義了主要的三個通用屬性事件的來源originService、事件的目的服務destinationService和隨機生成的全域性id。通過其構造方法可知,destinationService可以使用萬用字元的形式{serviceId}:{appContextId},兩個變數都省略的話,則通知到所有服務的所有例項。只省略appContextId時,則對應的destinationService為相應serviceId的所有例項。另外,註解@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
對應於序列化時,使用子類的名稱作為type;而@JsonIgnoreProperties("source")
表示序列化時,忽略source屬性,source定義在JDK中的EventObject
。
EnvironmentChangeRemoteApplicationEvent
用於動態更新服務例項的環境屬性,我們在基礎應用中更新cloud.version
屬性時,關聯到該事件。
public class EnvironmentChangeRemoteApplicationEvent extends RemoteApplicationEvent {
private final Map<String, String> values;
public EnvironmentChangeRemoteApplicationEvent(Object source, String originService,
String destinationService, Map<String, String> values) {
super(source, originService, destinationService);
this.values = values;
}
...
}
複製程式碼
可以看到,EnvironmentChangeRemoteApplicationEvent
事件類的實現很簡單。定義了Map型別的成員變數,key對應於環境變數名,而value對應更新後的值。
RefreshRemoteApplicationEvent
重新整理遠端應用配置的事件,用於接收遠端重新整理的請求。
public class RefreshRemoteApplicationEvent extends RemoteApplicationEvent {
public RefreshRemoteApplicationEvent(Object source, String originService,
String destinationService) {
super(source, originService, destinationService);
}
}
複製程式碼
繼承自抽象事件類RemoteApplicationEvent
,沒有特別的成員屬性。
AckRemoteApplicationEvent
確認遠端應用事件,該事件表示一個特定的RemoteApplicationEvent
事件被確認。
public class AckRemoteApplicationEvent extends RemoteApplicationEvent {
private final String ackId;
private final String ackDestinationService;
private Class<? extends RemoteApplicationEvent> event;
public AckRemoteApplicationEvent(Object source, String originService,
String destinationService, String ackDestinationService, String ackId,
Class<? extends RemoteApplicationEvent> type) {
super(source, originService, destinationService);
this.ackDestinationService = ackDestinationService;
this.ackId = ackId;
this.event = type;
}
...
public void setEventName(String eventName) {
try {
event = (Class<? extends RemoteApplicationEvent>) Class.forName(eventName);
} catch (ClassNotFoundException e) {
event = UnknownRemoteApplicationEvent.class;
}
}
}
複製程式碼
該事件類在RemoteApplicationEvent
基礎上,定義了成員屬性ackId、ackDestinationService和event。
ackId和ackDestinationService,分別表示確認的時間的id和對應的目標服務。event對應事件型別,確認事件能夠確認的必然是RemoteApplicationEvent
的子類,因此event屬性設值時需要進行檢查,如果轉換出現異常,則定義為未知的事件型別。這些事件可以被任何需要統計匯流排事件響應的應用程式來監聽。 它們的行為與普通的遠端應用程式事件相似,即如果目標服務與本地服務ID匹配,則應用程式會在其上下文中觸發該事件。
SentApplicationEvent
傳送應用事件,表示系統中的某個地方傳送了一個遠端事件。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonIgnoreProperties("source")
public class SentApplicationEvent extends ApplicationEvent {
private static final Object TRANSIENT_SOURCE = new Object();
private final String originService;
private final String destinationService;
private final String id;
private Class<? extends RemoteApplicationEvent> type;
protected SentApplicationEvent() {
// for serialization libs like jackson
this(TRANSIENT_SOURCE, null, null, null, RemoteApplicationEvent.class);
}
public SentApplicationEvent(Object source, String originService,
String destinationService, String id,
Class<? extends RemoteApplicationEvent> type) {
super(source);
this.originService = originService;
this.type = type;
if (destinationService == null) {
destinationService = "*";
}
if (!destinationService.contains(":")) {
// All instances of the destination unless specifically requested
destinationService = destinationService + ":**";
}
this.destinationService = destinationService;
this.id = id;
}
...
}
複製程式碼
可以看到該事件類繼承自ApplicationEvent
,它本身並不是一個RemoteApplicationEvent
事件,所以不會通過匯流排傳送,而是在本地生成(多為響應遠端事件)。想要審計遠端事件的應用可以監聽該事件,並且所有的AckRemoteApplicationEvent
事件中的id來源於相應的SentApplicationEvent
中定義的id。在其定義的成員屬性中,相比於遠端應用事件多了一個事件型別type,該型別限定於RemoteApplicationEvent
的子類。
UnknownRemoteApplicationEvent
未知的遠端應用事件,也是RemoteApplicationEvent
事件類的子類。該事件類與之前的SentApplicationEvent
、AckRemoteApplicationEvent
有關,當序列化時遇到事件的型別轉換異常,則自動構造成一個未知的遠端應用事件。
事件監聽器以及訊息的訂閱與釋出待後續更新。。