Spring Cloud構建微服務架構:消息驅動的微服務(核心概念)【Dalston版】

通過《Spring Cloud構建微服務架構:消息驅動的微服務(入門)》一文,相信大家對Spring Cloud Stream的工作模式已經有了一些基礎概念,比如:輸入、輸出通道的綁定,通道消息事件的監聽等。下面在本文中,我們將詳細介紹一下Spring Cloud Stream中是如何通過定義一些基礎概念來對各種不同的消息中間件做抽象的。

下圖是官方文檔中對於Spring Cloud Stream應用模型的結構圖。從中我們可以看到,Spring Cloud Stream構建的應用程序與消息中間件之間是通過綁定器Binder相關聯的,綁定器對於應用程序而言起到了隔離作用,它使得不同消息中間件的實現細節對應用程序來說是透明的。所以對於每一個Spring Cloud Stream的應用程序來說,它不需要知曉消息中間件的通信細節,它只需要知道Binder對應用程序提供的概念去實現即可,而這個概念就是在快速入門中我們提到的消息通道:Channel。如下圖案例,在應用程序和Binder之間定義了兩條輸入通道和三條輸出通道來傳遞消息,而綁定器則是作爲這些通道和消息中間件之間的橋樑進行通信。

綁定器

Binder綁定器是Spring Cloud Stream中一個非常重要的概念。在沒有綁定器這個概念的情況下,我們的Spring Boot應用要直接與消息中間件進行信息交互的時候,由於各消息中間件構建的初衷不同,它們的實現細節上會有較大的差異性,這使得我們實現的消息交互邏輯就會非常笨重,因爲對具體的中間件實現細節有太重的依賴,當中間件有較大的變動升級、或是更換中間件的時候,我們就需要付出非常大的代價來實施。

通過定義綁定器作爲中間層,完美地實現了應用程序與消息中間件細節之間的隔離。通過嚮應用程序暴露統一的Channel通道,使得應用程序不需要再考慮各種不同的消息中間件實現。當我們需要升級消息中間件,或是更換其他消息中間件產品時,我們要做的就是更換它們對應的Binder綁定器而不需要修改任何Spring Boot的應用邏輯。這一點在上一章實現消息總線時,從RabbitMQ切換到Kafka的過程中,已經能夠讓我們體驗到這一好處。

目前版本的Spring Cloud Stream爲主流的消息中間件產品RabbitMQ和Kafka提供了默認的Binder實現,在快速入門的例子中,我們就使用了RabbitMQ的Binder。另外,Spring Cloud Stream還實現了一個專門用於測試的TestSupportBinder,開發者可以直接使用它來對通道的接收內容進行可靠的測試斷言。如果要使用除了RabbitMQ和Kafka以外的消息中間件的話,我們也可以通過使用它所提供的擴展API來實現其他中間件的Binder

仔細的讀者可能已經發現,我們在快速入門示例中,並沒有使用application.properties或是application.yml來做任何屬性設置。那是因爲它也秉承了Spring Boot的設計理念,提供了對RabbitMQ默認的自動化配置。當然,我們也可以通過Spring Boot應用支持的任何方式來修改這些配置,比如:通過應用程序參數、環境變量、application.properties或是application.yml配置文件等。比如,下面就是通過配置文件來對RabbitMQ的連接信息以及input通道的主題進行配置的示例:

spring.cloud.stream.bindings.input.destination=raw-sensor-data

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=springcloud
spring.rabbitmq.password=123456

發佈-訂閱模式

在Spring Cloud Stream中的消息通信方式遵循了發佈-訂閱模式,當一條消息被投遞到消息中間件之後,它會通過共享的Topic主題進行廣播,消息消費者在訂閱的主題中收到它並觸發自身的業務邏輯處理。這裏所提到的Topic主題是Spring Cloud Stream中的一個抽象概念,用來代表發佈共享消息給消費者的地方。在不同的消息中間件中,Topic可能對應着不同的概念,比如:在RabbitMQ中的它對應了Exchange、而在Kakfa中則對應了Kafka中的Topic。

在快速入門的示例中,我們通過RabbitMQ的Channel進行發佈消息給我們編寫的應用程序消費,而實際上Spring Cloud Stream應用啓動的時候,在RabbitMQ的Exchange中也創建了一個名爲input的Exchange交換器,由於Binder的隔離作用,應用程序並無法感知它的存在,應用程序只知道自己指向Binder的輸入或是輸出通道。爲了直觀的感受發佈-訂閱模式中,消息是如何被分發到多個訂閱者的,我們可以使用快速入門的例子,通過命令行的方式啓動兩個不同端口的進程。此時,我們在RabbitMQ控制頁面的Channels標籤頁中看到如下圖所示的兩個消息通道,它們分別綁定了啓動的兩個應用程序。

而在Exchanges標籤頁中,我們還能找到名爲input的交換器,點擊進入可以看到如下圖所示的詳情頁面,其中在Bindings中的內容就是兩個應用程序綁定通道中的消息隊列,我們可以通過Exchange頁面的Publish Message來發布消息,此時可以發現兩個啓動的應用程序都輸出了消息內容。

下圖總結了我們上面所做嘗試的基礎結構,我們啓動的兩個應用程序分別是“訂閱者-1”和“訂閱者-2”,他們都建立了一條輸入通道綁定到同一個Topic(RabbitMQ的Exchange)上。當該Topic中有消息發佈進來後,連接到該Topic上的所有訂閱者可以收到該消息並根據自身的需求進行消費操作。

相對於點對點隊列實現的消息通信來說,Spring Cloud Stream採用的發佈-訂閱模式可以有效的降低消息生產者與消費者之間的耦合,當我們需要對同一類消息增加一種處理方式時,只需要增加一個應用程序並將輸入通道綁定到既有的Topic中就可以實現功能的擴展,而不需要改變原來已經實現的任何內容。

消費組

雖然Spring Cloud Stream通過發佈-訂閱模式將消息生產者與消費者做了很好的解耦,基於相同主題的消費者可以輕鬆的進行擴展,但是這些擴展都是針對不同的應用實例而言的,在現實的微服務架構中,我們每一個微服務應用爲了實現高可用和負載均衡,實際上都會部署多個實例。很多情況下,消息生產者發送消息給某個具體微服務時,只希望被消費一次,按照上面我們啓動兩個應用的例子,雖然它們同屬一個應用,但是這個消息出現了被重複消費兩次的情況。爲了解決這個問題,在Spring Cloud Stream中提供了消費組的概念。

如果在同一個主題上的應用需要啓動多個實例的時候,我們可以通過spring.cloud.stream.bindings.input.group屬性爲應用指定一個組名,這樣這個應用的多個實例在接收到消息的時候,只會有一個成員真正的收到消息並進行處理。如下圖所示,我們爲Service-A和Service-B分別啓動了兩個實例,並且根據服務名進行了分組,這樣當消息進入主題之後,Group-A和Group-B都會收到消息的副本,但是在兩個組中都只會有一個實例對其進行消費。

默認情況下,當我們沒有爲應用指定消費組的時候,Spring Cloud Stream會爲其分配一個獨立的匿名消費組。所以,如果同一主題下所有的應用都沒有指定消費組的時候,當有消息被髮布之後,所有的應用都會對其進行消費,因爲它們各自都屬於一個獨立的組中。大部分情況下,我們在創建Spring Cloud Stream應用的時候,建議最好爲其指定一個消費組,以防止對消息的重複處理,除非該行爲需要這樣做(比如:刷新所有實例的配置等)。

消息分區

通過引入消費組的概念,我們已經能夠在多實例的情況下,保障每個消息只被組內一個實例進行消費。通過上面對消費組參數設置後的實驗,我們可以觀察到,消費組並無法控制消息具體被哪個實例消費。也就是說,對於同一條消息,它多次到達之後可能是由不同的實例進行消費的。但是對於一些業務場景,就需要對於一些具有相同特徵的消息每次都可以被同一個消費實例處理,比如:一些用於監控服務,爲了統計某段時間內消息生產者發送的報告內容,監控服務需要在自身內容聚合這些數據,那麼消息生產者可以爲消息增加一個固有的特徵ID來進行分區,使得擁有這些ID的消息每次都能被髮送到一個特定的實例上實現累計統計的效果,否則這些數據就會分散到各個不同的節點導致監控結果不一致的情況。而分區概念的引入就是爲了解決這樣的問題:當生產者將消息數據發送給多個消費者實例時,保證擁有共同特徵的消息數據始終是由同一個消費者實例接收和處理。

Spring Cloud Stream爲分區提供了通用的抽象實現,用來在消息中間件的上層實現分區處理,所以它對於消息中間件自身是否實現了消息分區並不關心,這使得Spring Cloud Stream爲不具備分區功能的消息中間件也增加了分區功能擴展。

以下專題教程也許您會有興趣

本文內容部分節選自我的《Spring Cloud微服務實戰》,但對依賴的Spring Boot和Spring Cloud版本做了升級。

本文首發:http://blog.didispace.com/spring-cloud-starter-dalston-7-2/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章