當我們在說事件驅動的時候,我們在說什麼

2021-09-08 07:18:29 字數 4107 閱讀 3100

翻譯自martin fowler2023年初的文章what do you mean by 「event-driven」?。雖然這篇文章網上已經有一些翻譯,但是基本都是直譯,甚至有的翻譯比較晦澀。再加上最近工作中遇到不少事件驅動架構設計的問題,所以把這個再翻一下,並不是直譯,主要是加上一些自己的理解。

martin fowler是物件導向分析設計、重構等領域的頂級專家,也是敏捷開發的創始人之一,也是企業應用架構方面的頂級專家。

事件通知

這一模式就是乙個系統傳送一些事件訊息到另一些系統,以通知他們說我這個系統裡面的領域物件發生了改變。這個通知的乙個關鍵點是,我的源系統並不關心對方系統收到這些通知以後的結果,甚至說,根據不期望有返回結果。如果說,在有些業務場景下,需要知道對方處理的結果的話,那也應該是通過另乙個事件通知的邏輯來通知源系統。

所以,事件通知模式非常好的實現了系統之間的隔離性,而且很容易實現,我們只需要乙個訊息佇列就能實現,隨便乙個開源的mq伺服器就能滿足。

但是,這也有乙個潛在的問題,就是事件通知的這個流(也就是乙個事件從a系統傳送到b系統,b系統處理完以後,又傳送另乙個事件到c系統),也是很難管理或監控的,因為它散落在各個系統的**裡。所以,當我們的系統變得複雜,系統(或者說服務)越來越多的時候,這些系統之間的事件及其流向,就很難維護了。但是,即便是這些弊端,事件通知模式還是很有用的,因為它非常簡單。

使用事件通知還有乙個需要注意的問題是,對事件的定義。事件是指的原系統發生了某件事情,導致領域物件的改變。但是很多人在使用的過程中,往往將"事件"和"動作"混淆。動作是發起請求的人希望目標系統執行的"動作",而"通知"是源系統領域物件發生的事情。舉個例子來說,a是訂單服務,b是庫存服務,當有乙個訂單時,要通知庫存服務去更新庫存。那麼通知和命令是這樣的:

通知:a通知b有了乙個新訂單,b自己根據業務決定如何根據訂單處理庫存)

命令:a命令b修改庫存,這時候,a與b之間的關係就變了。

這在理解上很好理解,但是在實際開發中很容易忽略,需要引起注意。

有關事件通知,還有一點就是,事件不需要攜帶很多資料,而只是攜帶像id和對源資料的查詢鏈結之類的資料。還是用上面的訂單、庫存為例,訂單系統發出的事件中只有乙個訂單id和用來查詢訂單id的url,庫存服務收到該事件以後,用這個url,加上訂單id,獲取訂單的資訊,這個資訊是庫存服務需要知道最少資料,如訂單商品和數量。

個人認為,這種方式,雖然減少了訊息佇列中的資料傳輸,也減少了系統之間的資料結構的耦合性(目標系統需要知道原系統發出來的事件中的資料結構),但是,目標系統還是需要通過一定方式知道事件訊息的資料是什麼樣的,而且每次還要重新取資料,可能原系統為了它這個請求還要多寫乙個介面,有點得不償失。

攜帶狀態的事件傳遞

這種模式跟上面的比,就是把目標系統處理事件時需要用到的資料都放在事件訊息裡。這種方式解決了上面說的一些問題。可能會帶來的問題中,事件的儲存量的增加應該算不上問題了。但是乙個最大的問題就是,源系統和目標系統都需要知道事件中資料的結構。還有當事件中的資料結構發生改變,如增減欄位等,那需要目標系統也做相應修改。這一點本來就是不可避免的,即使是上面的不攜帶資料的方式,目標系統也需要更新最新的資料結構獲取資訊。

事件溯源

事件溯源模式的核心思想是,在乙個系統中,任何的狀態的變化,都需要產生乙個事件,並由這個事件觸發相應業務狀態的更改。在這種模式下,當前的業務狀態,是由這些事件以及它們的處理方法生成的。如果我們將這些事件儲存下來,在需要的時候,只要再重新觸發這些事件,讓這些事件的處理過程重新執行,就能夠重新生成業務狀態,甚至可以通過指定乙個事件,來重新生成某乙個時間點的資料狀態。這也是一些人常說的"歷史重現"。

有關事件溯源,有乙個錯誤的認識是,事件溯源不一定非要是非同步的。作者舉了乙個非常好的例子,是git資源庫。乙個git資源庫可以看作是乙個事件溯源的應用,我們提交的乙個個commit就是乙個個的事件,所有的commit都是依次、同步的作用在這個版本控制系統中,我們的資源庫中的最新的檔案,就是這些commit事件依次作用產生的結果。

對事件溯源的另乙個誤區是,在事件溯源系統中的每個請求,在處理這個事件的時候,不需要知道整個系統的所有事件,而只需要知道它自己感興趣的那一類事件。還是以git資源庫為例。git是乙個分布式的版本管理系統,如果我編輯了資源庫中的乙個檔案,修改完以後commit;這時另乙個人在他的電腦也修改了這個資源庫中的另乙個檔案,也commit到他的本地資源庫。當我們兩個人把這兩個commit同步(push)到某個伺服器上的資源庫的時候,並不會衝突,git會根據我們的提交的時間,生成相應的commit。這時候伺服器上的資源庫裡面,提交日誌(相當於event log)裡面看到的就是合併後的commit。

對於這個用git資源庫來模擬事件溯源,可以這麼理解,每個人都可以把git資源庫clone到本地,相當於將這個基於事件溯源的應用系統進行分布式部署。假設我們每個人在修改檔案的時候,只允許修改乙個檔案,提交後才能修改另乙個檔案。每個提交的commit事件中都帶有這個檔案的id。這乙個個的檔案相當於事件溯源系統中的領域物件,如乙個訂單資訊,乙個商品資訊,每個訂單每個商品都有它的全域性唯一id。我們的每個事件就是在某乙個領域物件上的更新操作,例如乙個訂單事件就是更新一條訂單資料的狀態。

在事件溯源系統中,領域物件的狀態是根據跟他相關的事件生成的。也就是說,如果我們不儲存業務狀態資料,那麼在每次處理乙個訂單事件的時候,都要取出這個訂單的所有事件,然後根據這些事件生成當前的業務狀態資訊,然後再處理新的那個事件。

所以,系統在處理每個事件的時候,只需要獲取這個事件所屬的物件相關的事件,而不需要獲取所有的事件。就好像我們在git中編輯乙個檔案,就先獲取有關這個檔案的commit記錄,根據這些提交的commit事件,生成最新的檔案,然後在這個檔案上編輯,編輯完成後再提交乙個新的commit事件。只不過,我們的git資源庫會將本地的最新的檔案狀態儲存下來,所以我們不需要每次都重新根據commit生成檔案。在git中我們可以對某乙個版本打tag,在事件溯源模式中,我們也可以用類似的方式建立快照,將領域物件的當前狀態儲存成snapshot快照,這樣就不需要每次都獲取所有相關事件重新生成業務狀態了。

事件溯源模式有很多有意思的特性,比如我們可以將整個系統看做是乙個有版本管理功能的業務系統。根據上面說的"開始重現"功能,我們可以將系統的業務狀態充值到任何乙個時間點。我們甚至可以在重現歷史的過程中,新增一些假想的業務資料,用於進行一些業務驗證。

當然,事件溯源模式也有一些問題,如上面說的"歷史重現",如果我們的系統需要依賴外部系統,那麼該系統在重新歷史的時候,其他的系統已經是最新的狀態,這就時空錯亂了,就會有錯誤。還有乙個問題就是事件的資料結構的改變問題。如果事件的資料結構改變,但是歷史的事件中,相應的資料就會缺失,或者多餘。那麼系統就需要既能處理歷史版本的事件,也能處理新版本的事件。這無疑為我們的事件設計、和系統設計都帶來一定的困難。

cqrs

command query responsibility segregation (cqrs)簡單來說就是讀寫分離。也就是去操作使用的資料,跟寫操作使用的資料不是同乙個資料。雖然從根本上講,實現cqrs模式,不一定要使用事件溯源模式來實現。但是一般情況下,當人們說cqrs的讀寫分離的時候,基本上都是通過事件溯源模式來實現的。

要實現cqrs模式,一般都是通過事件溯源模式來進行資料更新操作,也就是所有的業務資料狀態的變更都通過事件來觸發。然後,對於每個事件,領域物件處理完該事件的同時,還有另乙個事件處理器,根據這個事件將最新的業務狀態更新到資料庫中。然後,對於所有的讀操作,都從這個儲存業務狀態的資料庫中獲取。通過這種業務狀態的資料和事件資料的分離,相互之間不會出現資料的鎖,可以實現很高的吞吐量。

可以發現,cqrs模式,看起來很優雅,但是實現起來往往比較複雜,如果沒有成熟的框架,而自己去實現,肯定會非常困難。

合理使用這些模式

至於說到如何合理的使用這些模式,首先要弄清楚這些模式。例如文中作者說到,有人說事件溯源模式給他們帶來了災難,每個事件都要處理兩次,(乙個是更新領域物件,一次是更新read model),實際上是因為他們把事件溯源跟cqrs搞混了。使用事件溯源模式,不一定要用讀寫分離的方式使用。還有人說系統中大量的非同步通訊,導致了大量的複雜性(有些時候我們需要乙個事件的處理結果)。但是事件溯源模式不一定非要是非同步發處理,這只是跟我們的實現有關,我們大可以在需要的地方使用同步的方式。

我個人覺得,事件溯源+cqrs模式,確實是非常的優雅,我之前用過一點axon框架,可以用來實現事件溯源+cqrs模式,而且這個框架的設計也很清晰,使用起來也比較容易。但是,這種方式確實會增加**量,因為乙個事件需要有2個處理函式,分別更新業務狀態資料和領域物件資料,還要定義一堆的命令和事件。除了axon以外,也有一些別的框架,希望隨著事件驅動架構的應用越來越多,相應的框架也會越來越多,越來越成熟。

當我們在說事件驅動的時候,我們在說什麼

martin fowler是物件導向分析設計 重構等領域的頂級專家,也是敏捷開發的創始人之一,也是企業應用架構方面的頂級專家。事件通知 這一模式就是乙個系統傳送一些事件訊息到另一些系統,以通知他們說我這個系統裡面的領域物件發生了改變。這個通知的乙個關鍵點是,我的源系統並不關心對方系統收到這些通知以後...

當我們說資料探勘的時候我們在說什麼

開頭下定語 統計學習 現在市面上談論到的資料探勘基本上都是基於統計學習的監督學習或非監督學習問題。尤其以監督學習應用面更廣。統計學習的應用 分類問題 客戶分類模型 異常鑑別 影象識別等 標註問題 資訊抽取 自然語言處理等 統計學習的一般流程 步驟一 得到乙個有限的資料集合 涉及到以下多個流程 1.資...

當我們在談論資料資產保護的時候,我們是在說什麼?

在大資料時代的背景下,以ai 大資料 物聯網 雲計算等為標誌的新一輪科技變革正在進行,資料的價值和安全越來越受到重視。可以說,誰掌握資料,誰就掌握了未來。那麼如何定義資料 管理資料 保護資料,實現資料的價值,就成為近來各界熱切關注的議題。要實現對資料資產的管理和保護,首先要對資料進行分類。這裡將以卡...