雲原生事件驅動引擎(RocketMQ-EventBridge)應用場景與技術解析

作者:羅靜

在剛剛過去的 RocketMQ Summit 2022 全球開發者峯會上,我們對外正式開源了我們的新產品 RocketMQ-Eventbridge 事件驅動引擎。

RocketMQ 給人最大的印象一直是一個消息引擎。那什麼是事件驅動引擎?爲什麼我們這次要推出事件驅動引擎這個產品?他有哪些應用場景,以及對應的技術方案是什麼?

今天我們就一起來看下,整篇文章包含三部分:

第一部分,我們一起看下什麼是事件。

第二部分,和大家一起看看,事件有哪些不一樣的“超能力”,使用這些“超能力”呢,我們又能幹些什麼?

第三部分,我們講一下 RocketMQ 給出的關於事件的解決方案,也是我們這次開源的項目:RocketMQ-EventBridge。

什麼是事件

大家自己可以先在腦袋裏想一下,什麼是事件?我們給事件下的一個定義是:

過去已經發生的事,尤其是比較重要的事。

A thing that happens, especially one of importance.

這個很好理解。比如說,昨天下午我做了一次核酸檢測;今天上午又吃了一個冰激淋。這些都是過去已經發生的事件。但是,如果我再問:事件跟消息有什麼區別?這個時候,大家是不是覺得事件這個定義,好像又不那麼清晰?

剛纔說的那些事件,是不是也可以理解爲消息啊?如果,老張給我發送了一條短信,這個算是事件,還是消息啊?平常開發過程中,“什麼時候使用消息,什麼時候使用事件?”

不過,在回答這個問題之前,我們一起來看一個典型的微服務。

1.png

一個微服務系統和外部系統的交互,可以簡單分爲兩部分:一是接收外部請求(就是圖中上面黃色的部分);二是是調用外部服務(就是圖中下面綠色的部分)。

接收外部請求,我們有兩種方式:一種是提供 API,接收外部發過來的 Query 請求和 Commond 請求;另外一種是主動訂閱外部 Command 消息。這兩類操作,進入系統內部之後呢,我們常常還會,調用其他爲微服務系統,一起協同處理,來完成一個具體的操作。當這些操作,使得系統狀態發生改變時,就會產生事件。

這裏呢,我們把從外部接收到的 Command 消息,和系統內部產生的事件,都稱之爲消息。

我們總結一下,消息和事件的關係是這樣的:消息包含兩部分,Command 消息和 Event 消息

1、看圖中左半部分,Command 是外部系統發送給本系統的一條操作命令;

2、再看圖中右半部分,Event 則是本系統收到 Command 操作請求,系統內部發生改變之後,隨之而產生了事件;

2.png

所以,事件和消息是不同的,事件可以理解爲是一種特殊的消息。其特殊的點,主要在 4 個地方:

已發生、且不可變

事件,一定是“已發的”。“已發生”的代表什麼呢?不可變的。我們不可能改變過去。這個特性非常重要,在我們處理事件、分析事件的時候,這就意味着,我們絕對可以相信這些事件,只要是收到的事件,一定是系統真實發生過的行爲。而且是不可修改。

對比 Command 和 Query。Command 的中文是什麼?命令。很顯然,它是還沒有發生的,只是表達了一種期望。我們知道“期望的”,不一定會成功發生。

比如:把廚房的燈打開、去按下門鈴、轉給 A 賬戶 10w……

這些都是 Commond,都是期望發生的行爲。但是,最終有沒有發生?並不知道。

Event 則是明確已經發生的事情。比如:廚房燈被打開了、有人按了門鈴、A 賬戶收到了 10w……

再對比 Query,它則是查詢系統當前狀態的一種請求,比如:廚房的燈是打開着的、門鈴正在響、查下賬戶顯示餘額 11w……

無期望的

這個怎麼理解?事件是客觀的描述一個事物的狀態或屬性值的變化,但對於如何處理事件本身並沒有做任何期望。

相比之下,Commond 和 Query 則都是有期望的,他們希望系統做出改變或則返回結果,但是 Event 呢,它只是客觀描述系統的一個變化。

我們看一個例子:交通信號燈,從綠燈變成紅燈,事件本身並沒有要求行人或汽車禁止通行,而是交通法規需要紅綠燈,並賦予了其規則。

3.png

所以,系統一般不會定向的、單獨向另外一個系統發送事件,而是統一的告訴“事件中心”,“事件中心”呢,那裏面有各個系統上報上來的,各式各樣的事件。系統會向事件中心說明:自己這個系統,會產生哪些事件呀,這些事件的格式是怎麼樣的呀。

別的系統如果感興趣呢,就可以來主動訂閱這些事件。真正賦予事件價值的,是事件消費者。事件消費者想看看,某個系統發生了什麼變化呀?OK,那他就去訂閱這些事件,所以事件是消費者驅動的。

這跟消息有什麼區別呢?Commond 消息的發送和訂閱,是雙方約定好的,外人不知道,往往是以文檔或代碼的形式,大家按約定好的協議,發送和訂閱消費,所以消息是生產者驅動的。

我們打個比喻,事件就像市場經濟,商品被生產出來,具體有什麼價值,有多大價值,很大程度上看其消費者。我們能看到系統中各種各樣的事件,就像櫥窗裏擺放了各種各樣的商品。而 Commond 消息呢,有點像計劃經濟,一出生就帶着很強的目的性,我就是要“分配”給誰消費。

天然有序

事件的第三個特性是:“天然有序”。含義:同一個實體,不能同時發生 A 又發生 B,必有先後關係;如果是,則這兩個事件必屬於不同的事件類型。

比如:針對同一個交通信號燈,不能既變成綠燈,又變成紅燈,同一時刻,只能變成一種狀態。

4.png

大家可能發現了一點,這裏其實隱藏了事件的一個額外屬性:因爲天然有序,跟時間軸上的某一時刻強綁定,且不能同時發生,所以它一定是唯一的。

如果我們看到了兩個內容一樣的事件,那麼一定是發生了兩次,而且一次在前,一次在後。(這對於我們處理數據最終一致性、以及系統行爲分析都很有價值:我們看到的,不光光是系統的一個最終結果,而是看到變成這個結果之前的,一系列中間過程)

具像化

事件的第四個特性是:“具象化”的。

事件會盡可能的把“案發現場”完整的記錄下來,因爲它也不知道消費者會如何使用它,所以它會做到儘量的詳盡,比如:

●是由誰產生的事件?Subject

●是什麼類型的事件?Type

●是誰發送的事件?Source

●事件的唯一性標誌是什麼?Id

●什麼時候發生?Time

●事件的內容是什麼?Data

●事件的內容有哪些信息?Dataschema

我們還是以交通信號燈舉例子:

5.png

對比我們常見的消息,因爲上下游一般是確定的,常常爲了性能和傳輸效率,則會做到儘可能的精簡,只要滿足“計劃經濟”指定安排的消費者需求即可。

總結一下,事件上面的 4 個特性,是對事件巨大的一個屬性加成,讓事件擁有了跟普通消息不一樣的“超能力”。使事件,常常被用到 4 個典型場景:事件通知、事件溯源、系統間集成和 CQRS。

6.png

下面讓我們一個個展開,具體看看這些應用場景。

事件的典型應用場景

事件通知

事件通知是我們系統中很常見的一個場景。比如:用戶下單事件通知給支付系統;用戶付款事件通知給交易系統。

這裏,讓我們回到一開始信號燈那個例子。當交通信號燈,從紅燈變成綠燈時,可能存在很多系統都需要這個信息。

方式 1:發送方主動調用,適配接收方

一種最簡單的方式是,我們依次 call 每個系統,並把信息傳遞出去。比如:信號燈系統,主動調用地圖導航的 API 服務、調用交警中控的 API 服務,調用城市大腦的 API 服務,把紅綠燈變化信號發送出去。

7.png

但我們都知道,這個設計非常糟糕。尤其當系統越來越多時,這無疑是災難的,不僅開發成本高,而且其中一個系統出現問題,可能會 hang 住整個服務,則導致調用其他系統都會受到影響。

方式 2:接收方主動訂閱,適配發送方

一個很自然的解決方案是,我們將這些信息發送到中間消息服務 Broker,其他系統如果有需要,則主動去訂閱這些消息即可。

8.png

這個時候,信號燈系統與其他系統並沒有直接的調用依賴,交警中控服務、地圖導航服務、城市大腦服務,只要按照約定的協議,去訂閱信號燈的消息,並解析這些信息即可。

但是,這裏同樣存在一個問題:這個架構中,是以“信號燈”爲中心。消費者需要理解發送者的業務領域,並主動添加適配層,(就是圖中白色迴旋鏢部分),將消息轉化爲自己業務領域內的語言。但對於每一個微服務來說,他都希望都是高內聚低耦合的。

如果交警中控需要全國的信號燈數據,但是每個地域的消息格式又不一樣,這就意味着,交警中控需要適配每一個地域的協議,做一層轉換。而且萬一後面變化了怎麼辦?想想就知道這個運維成本有多可怕。

9.png

那是否交警中控系統,可以要求全國所有紅綠燈系統,都按同一種數據協議給到自己呢?不好意思,這些信號燈數據地圖服務也在用,城市大腦也在用,不能更改。

方式 3:引入事件,Borker 根據接收方協議,進行靈活適配

但如果使用事件,就不一樣了。因爲事件是“無期望的”,“具像化的”,天然的保留了案發現場儘可能多的信息,且更加規範標準,對於消費者(也就是交警中空)來說,可以輕易將不同省份,收集上來的事件,輕易組裝成,符合自己業務要求的格式。

10.png

而且,這一組裝,是在中間層 Broker 發生的。對於交警中控來說,它只需要,按照自己業務領域的設計,提供一個接收事件的 API,然後其他事件,通過 Broker,主動投遞到這個 API 上即可。從頭到尾,對交警中控系統,沒有一行適配外部業務的代碼。

所以,這種方式有 3 個明顯的優勢:

1、只關注自己業務領域本身,不需要做適配外部的代碼;

2、所有對系統的變更,收斂到 API,爲唯一入口;同一個 API,可能既是用來接收事件的,也可能同時用於控制檯操作;

3、因爲事件是推送過來的,所以,也不需要像之前一樣,引入一個 SDK,和 Broker 發生連接,獲取消息,降低了系統的複雜度。

這樣,我們一開始的圖,就會變成這個樣子:交通信號燈產生事件,投遞到事件中心,其他需要這些事件的消費者,在事件中心訂閱,再由事件中心,按照他們期望的事件格式,主動投遞過去。

11.png

讓我們再來回顧下整個過程:

12.png

第 1 幅圖:一開始,我們通過強依賴的方式,讓信號燈系統,主動將信息發送給各個系統。那這張圖裏,我們是以各個下游服務爲中心,信號燈系統去適配各個下游服務。

第 2 幅圖:後來,我們採用傳統消息的方式,對調用鏈路進行了解耦,兩邊系統不再直接依賴了,但是依舊會存在業務上的依賴。消費者需要去理解生產者的消息格式,並在自己系統內部,進行轉換適配。所以,這裏其實是以生產者爲中心。

第 3 幅圖:最後,我們引入了事件通知的方式,對於這種方式,生產者和消費者,他們都只需要關注自己系統本身就可以了。生產者,生產什麼樣的事件,消費者,消費什麼樣的數據格式,都各自以自己的業務爲中心,不需要爲對方做適配。真正做到我們說的高內聚低耦合,實現徹底的完全解耦。

現在,回到我們一開始提到的典型微服務模型,對於有些場景,我們就可以變爲下面這種方式:對微服務的變更操作,統一收斂到 API 操作入口,去掉 Commond 消息入口。收斂入口,對於我們維護微服務,保障系統穩定性,常常非常有好處的。

13.png

事件溯源

事件溯源是什麼?事件溯源簡單理解就是讓系統回到過去任意時刻。那怎麼樣,才能讓系統可以回到過去呢?很簡單,首先系統所有發生的變化,都得以事件的方式記錄下來;然後,我們就可以通過回放事件的方式,回到過去任何一個時刻。

那爲什麼只有事件才能做這個事,其他普通消息不行呢?這個還是要回到我們剛纔說的幾個事件特性:已發生不可變的、天然有序且唯一的、而且是非常詳細具體的,完整的記錄了事件的案發現場。所以,對於事件溯源這個場景,事件可以說是系統的一等一的公民。

舉個例子:比如說,如果我們能夠完整地收集路上的各種事件信息,包括信號燈、車量、天氣、擁堵路況等等,那麼,我們就可以“穿越時間”,回到交通現場,重新做一次決策。比如,在智慧交通場景,當我們想去驗證一個調度算法的時候,我們就可以回放當時發生的所有事件,來重現現場。

14.png

大家可能覺得這個很神奇,但是,其實我們平常一直有接觸,大家知道是什麼嗎?就是我們常用的代碼版本-管理系統,比如:github。

這裏有大家可能會問,如果一個系統積讚了很多事件,想重放是不是得很久?比如在一些交易場景,每天都會產生大量的事件,那應該怎麼處理呢?這裏呢,系統一般每天晚上都會打一份快照。如果系統意外宕機,想回到某一個時刻,就可以把前一天的快照取出,然後再重新跑下當天的事件,即可恢復。而白天呢,所有的事件都是在內存中進行處理,不會跟數據庫交互,所以系統性能非常快,只有事件會落盤。

當然,事件溯源也不是適合所有場景,它有優點也有缺點,詳細看上圖。

系統間集成

剛纔講的第1個場景:事件通知,一般涉及到兩個上下游團隊的協作開發;講的第 2 個場景:事件溯源,則一般是 1 個團隊內的開發;但系統間集成,則往往面對的是三個業務團隊的協作開發。這個怎麼理解呢?

其實這個也很常見:比如公司裏購買了 ERP 系統,同時也購買了外部考勤系統、外部營銷系統服務等等。這些系統都有一個共同點,是什麼?都不是我們自己開發的,是而買來的。

15.png

如果我們想把 ERP 系統的人員信息,實時且自動同步到考勤系統中去怎麼辦?其實這個是有點麻煩的,因爲這些都不是我們自己開發的。

1、我們不能修改 ERP 系統的代碼,主動去調用考勤系統,把人員變更信息發送過去;

2、也不能修改考情繫統的代碼,主動去調用外部 ERP 系統的 API;

但是我們可以通過事件總線,藉助 webhook 或則標準 API 等等方式,收集上游的 ERP 系統產生的人員變更事件,然後進行過濾和轉換,推送到下游考勤系統中去,當然,這裏也可以是內部自研服務。

所以,現在的研發模式變成了:事件中心管理了所有 SaaS 服務,包括內部自研系統產生的所有事件。然後呢,我們只需要在事件中心,尋找我們需要的事件,進行訂閱,對 SaaS 服務和內部自研系統,進行簡單服務編排,即可完成開發。

CQRS

CQRS 中的 C 代表 Command,Command 什麼意思?就是明令,一般包含:Create/Update/Delete,Q 代表 Query,是指查詢。所以 CQRS 本質是讀寫分離:所有的寫操作,在圖中左邊的系統中完成,然後將系統因爲 Command 產生變化的事件,同步到右邊的查詢系統。

16.png

這裏同學可能有疑問,這跟數據庫的讀寫分離有什麼區別?數據庫讀寫分離也是提供一個寫的 DB,一個讀的 DB,兩邊做同步。對吧…

那這裏很大的一個區別是:對於數據庫的讀寫分離,是以數據庫爲中心,兩邊的數據庫是一模一樣的,甚至數據的存儲結構也是一模一樣的。

但是對於 CQRS 的讀寫分離場景,是以業務爲中心,兩邊存儲的數據結構格式,往往是不一樣的,甚至數據庫都不是同一種。完全圍繞各自的讀寫業務邏輯,設計最佳技術選型。對於寫場景,爲了保障事務,我們可能使用關係性數據庫;對於讀的場景,我們爲了提高性能,我們可能會使用 Redis、HBase 等 Nosql 數據庫。

當然 CQRS 也不是適合所有場景,他往往比較適合:

●希望同時滿足高併發的寫、高併發的讀;

●寫模型和讀模型差別比較大時;

●讀/寫比非常高時;

我們剛纔講了事件的 4 個應用場景,但是,事件不是萬能的,就像軟件研發也沒有銀彈,有很多場景也並不適合-使用事件。包括:

  1. 強依賴 Response 的同步調用場景;

  2. 要求服務調用保持事務強一致性的場景。

RocketMQ 關於事件的解決方案

需要什麼樣的能力?

首先,按照之前講到的事件應用場景,我們整理下,如果我們做好事件驅動這塊,我們的系統,需要具備什麼樣的能力呢?

17.png

第一,我們肯定得有一個事件標準,對吧…因爲,事件不是給自己看的,也不是給他看的,而是給所有人看的。剛纔,我們也講到事件是無期望的,它沒有明確的消費者,所有都是潛在的消費者,所以,我們得規範化事件的定義,讓所有人都能看得懂,一目瞭然。

第二,我們得有一個事件中心,事件中心裏面有所有系統,註冊上來的各種事件,(這個跟消息不一樣,我們沒有消息中心,因爲消息一般是定向的,是生產者和消費者約定的,有點像計劃經濟,消息生產出來的時候,帶着很強的目的性,是給誰誰消費的。而事件有點像市場經濟,事件中心呢,)這個有點類似市場經濟大賣場,玲琅滿目,裏面分類擺放了各種各樣的事件,所有人即使不買,也都可以進來瞧一瞧,看一看,有哪些事件,可能是我需要的,那就可以買回去。

第三,我們得有一個事件格式,用來描述事件的具體內容。這相當於市場經濟的一個買賣契約。生產者發送的事件格式是什麼,得確定下來,不能總是變;消費者以什麼格式接收事件也得確定下來,不然整個市場就亂套了。

第四,我們得給消費者一個,把投遞事件到目標端的能力。並且投遞前,可以對事件進行過濾和轉換,讓它可以適配目標端 API 接收參數的格式,我們把這個過程呢,統一叫做訂閱規則。

第五,我們還得有一個存儲事件的地方,就是最中間的事件總線。

事件標準

關於剛纔提到的第一點事件標準,我們選取了 CNCF 旗下的開源項目 CloudEvents,目前已被廣泛集成,算是一個事實上的標準。

18.png

它的協議也很簡單,主要規範了 4 個必選字段:id,source、type、specversion;以及多個可選字段:subject、time、dataschema、datacontenttype和data。上圖右邊,我們有一個簡單的例子,大家可以看下,這裏就不具體展開了。

另外,事件的傳輸也需要定義一種協議,方便不同系統之間的溝通,默認支持三種 HTTP 的傳輸方式:Binary Content Mode、Structured Content Mode 和 Batched Content Mode。通過 HTTP 的 Content-Type,就可以區分這三種不同的模式。其中前兩種,都是傳遞單個事件;第三種則是傳遞批量事件。

19.png

事件 Schema

事件的 Schema,用來描述事件中有哪些屬性、對應的含義、約束等等信息。目前我們選取了 Json Schema. 和 OpenAPI 3.0,根據事件的 Schema 描述,我們可以對事件進行合法性校驗。,當然 Schema 本身的修改,也需要符合兼容性原則,這裏不作具體展開。

事件過濾和轉換

關於事件的過濾和轉換,我們提供了 7 種事件過濾方式和 4 種事件轉換方式,詳細可以下圖描述:

20.png

技術架構

我們 RocketMQ 圍繞事件驅動推出的產品,叫做 EventBridge,也是我們這次要開源的新產品。

他的整個架構可以分爲兩部分:上面是我們的控制面、下面是我們的數據面。

21.png

控制面中最上面的 EventSource 是各個系統註冊上來的事件源,這些事件可以通過 APIGateway 發送事件到事件總線,也可以通過配置的 EventSource,生成 SouceRuner,主動從我們的系統中,去拉取事件。事件到達事件總線 EventBus 之後,我們就可以配置訂閱規則了 EventRule,在規則 EventRule 裏我們設置了事件怎麼過濾,以及投遞到目標端前,做哪些轉換。系統基於創建的規則會生成 TargetRunner,就可以將事件推送到指定的目標端。

那這裏 SouceRuner 和 TargetRunner 是什麼呢?我們具體能對接哪些上下游 Source 和 Target?

這些我們都可以在下面的 SourceRegister 和 TargetRegister 提前進行註冊。

所以 EventBridge 的數據面是一個開放的架構,他定義了事件處理的SPI,底下可以有多種實現。比如,我們把 RocketMQ 的 HTTPConnector 註冊到 EventBridge 中,那我們就可以把事件推送到 HTTP 服務端。

如果我們把 Kafka 的 JDBC Connector 註冊到 EventBridge 中,我們就可以把事件推送到數據庫。

當然,如果你的系統不是通用的像 HTTP/JDPC 等協議,也可以開發自己的 Connector,這樣就能將事件實時同步到 EventBridge,或則接收來自 EventBridge 的事件。

除此之外,我們還會有一些附加的運維能力,包括:事件追蹤、事件回放、事件分析、事件歸檔。

RocketMQ-EventBridge 與雲上

在所有開源的,與其他上下游系統做集成的 Connector 當中,我們有一個特殊的 Connector,叫:EventBridgeConnector,通過它可以方便的和阿里云云上的事件總線進行集成。這裏有兩個典型的應用場景:

第一個場景是:IDC 系統內部產生的事件,不僅可以用來做內部系統間的解耦,還可以實時同步到雲上,驅動雲上的一些計算服務,比如通過雲上 Maxcompute 對內部產生的事件進行離線分析,或則驅動雲上的圖像識別服務,實時分析事件中標註的圖片。

第二個場景是:如果 IDC 內部使用到了自建 MQ,我們同樣可以通過 MQConnector 和 EventBridgeConnector,實時同步事件到雲上,逐步將內部自建 MQ,遷移到雲上MQ。

22.png

生態發展

關於 EventBridge 的未來方向,我們希望是在開源,構建一個支持多雲架構的事件總線生態。這個怎麼理解?簡單來說,我們希望在不同雲廠商之間,包括雲廠商和內部 IDC 系統之間,可以通過事件,來打破圍牆,實現互通。雖然,這幾年雲計算髮展很快,但是對於一些特別大的客戶來講,有時候並不希望跟某家雲廠商強綁定。這不光是市場充分競爭的結果,也是大客戶一種降低風險的手段。所以,這個時候,如何在不同雲廠商之間,包括雲廠商系統和自己內部 IDC 系統之間,靈活的交互,甚至靈活的遷移,是企業非常重要的一個訴求。

23.png

當然,實現這個是有一定難度的。不過如果我們在進行企業架構設計的時候,是基於事件驅動架構進行設計開發——不同系統之間的交互,圍繞事件展開,就會容易很多。

事件,在這裏,就好比一種通用語言,通過這個通用語言,就可以實現和不同系統之間的溝通交流。比如:用 IDC 系統內部的事件,去驅動阿里雲上服務;甚至用阿里雲上的事件,去驅動 AWS 上的服務運行;

爲了實現這個目標,我們在和不同雲廠商,不同 SaaS 系統服務商,進行系統間集成的時候,需要開發與之對應的連接器。

也歡迎大家,一起來共建 RocketMQ-EventBridge 的生態。

源碼地址:

https://github.com/apache/rocketmq-eventbridge

感興趣的小夥伴們可以掃描下方二維碼加入釘釘羣討論(羣號:44552972)

24.png

點擊此處,進入 EventBridge 官網瞭解更多信息~

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