Spring Cloud Bus中的事件的訂閱與釋出(一)

aoho發表於2018-02-14

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-amqpspring-cloud-starter-bus-kafka其中的一種,分別對應於binder的兩種實現。根據上一節的基礎應用,我們總結出Spring Cloud Bus的主要功能如下兩點:

  • 對指定主題springCloudBus的訊息訂閱與釋出。
  • 事件監聽,包括重新整理事件、環境變更事件、遠端應用的ack事件以及本地服務端傳送事件等。

下面我們以這兩方面作為主線,進行Spring Cloud Bus的原始碼分析。本文主要針對事件的訂閱戶釋出。

事件的訂閱與釋出

事件驅動模型

這部分需要讀者首先了解下Spring的事件驅動模型。我們在這邊簡單介紹下設計的主要概念,幫助大家易於理解後面的內容。

event-source
事件驅動模型

Spring的事件驅動模型由三部分組成:

  • 事件:ApplicationEvent,繼承自JDK的EventObject,所有事件將繼承它,並通過source得到事件源。
  • 事件釋出者:ApplicationEventPublisher及ApplicationEventMulticaster介面,使用這個介面,我們的Service就擁有了釋出事件的能力。
  • 事件訂閱者:ApplicationListener,繼承自JDK的EventListener,所有監聽器將繼承它。

事件的定義

Spring的事件驅動模型的事件定義均繼承自ApplicationEventSpring Cloud Bus中有多個事件類,這些事件類都繼承了一個重要的抽象類RemoteApplicationEvent,我們看一下事件類的類圖:

bus-event
各種事件的定義

涉及的事件類有:代表了對特定事件確認的事件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事件類的子類。該事件類與之前的SentApplicationEventAckRemoteApplicationEvent有關,當序列化時遇到事件的型別轉換異常,則自動構造成一個未知的遠端應用事件。

事件監聽器以及訊息的訂閱與釋出待後續更新。。

訂閱最新文章,歡迎關注我的公眾號

微信公眾號

參考

Spring Cloud Bus-v1.3.3

相關文章