用校車系統理解事件驅動架構

很小的時候,我就被系統迷住了。尤其是一個系統的優雅性最令我著迷。完美的執行、可預見性,一如 校車系統的天才創意 。
校車系統是如此讓我著迷,以至於童年的我幾乎每天會在家裡的地板上覆刻這個過程。當時的我只是認為這個為8歲孩子設計的嚴謹的交通系統非常棒,但我很快就瞭解到,世界上有各種各樣的系統,有良好設計的系統,也有的系統沒有任何設計,有些設計比另一些設計要更好。但最重要的是,系統存在的目標是為了解決問題。
就校車系統來說,要解決的問題是:每年如何讓700名學生往返他們的家和學校大樓182次?
每到放學的時候,就會有三排校車停在校園裡,亮著黃燈,引擎轟鳴。放學鈴聲響起,學生們蜂擁而出,急急忙忙地登上每天下午都在同一地點的巴士汽車。
一旦所有的車都坐滿了,車門也都關好的,有一位老師就會給打頭的司機打個手勢,一條滿載了小學生的黃色鋼鐵佇列將沿著大山駛上主幹道,這個時候,校長會攔停過往的車輛以便每輛巴士都能迅速離開,把孩子們送回家。
我將把這個比喻應用到本文關於事件驅動架構的其餘內容中。因此,請注意,這個比喻的各個組成部分是:
● 中介:即指揮交通的校長。
● 渠道:即校車和行動路線。
● 佇列:即停車場和停車標誌。
● 事件:即學生。
一、事件驅動架構
近年來,計算領域的各種趨勢已經浮出水面:大資料、容器、無伺服器應用程式、微服務和事件驅動架構(EDA)。相比單體應用,事件驅動架構讓企業可以更快建立更易於管理的可伸縮解決方案,所以越來越受歡迎。
越來越多的企業正在意識到,隨著系統必須處理的資料呈指數級增長,傳統的RDBMS無法處理這麼大的資料量,資訊的處理需要長時間執行的基於批處理的ETL來提取業務洞察力。批處理操作用於資料倉庫當然沒有什麼問題,但是實時洞察力的需求對於實現企業想要的結果至關重要。
N層架構示例
在20世紀中期,N層架構大行其道。企業大多會構建由RDBMS支援的單體應用程式碼庫,所有CRUD都是針對RDBMS執行的。但應用程式越來越複雜,它們的依賴關係圖譜實際上是不可能理解的。又或者,拼湊了那麼多點對點的整合,從而建立了一個脆弱的解決方案。沒有人會責怪軟體工程師,畢竟,面向物件程式設計(Object-Oriented Programming,OOP)鼓勵程式碼重用,而在當時,確實只需要服務X來與服務Y對話即可。這很糟糕。
更好的方法
很多企業正開始從單體架構遷移到微服務架構。業務邏輯及相關服務與事件處理器的分離降低了架構的複雜性。服務可以單獨開發和部署,不必知道其他服務的存在。微服務架構需要從系統的角度來促進服務間通訊。在我看來,為此進行一些初始投資是值得的。
本質上,事件驅動架構促成了去中心化的平臺。服務甚至不必駐留在同一個系統或資料中心,也不必由同一個組織所擁有。如果校車系統需要額外的巴士,例如,由於校外活動在不同的學區舉行,校車系統可以要求一個或多個系統提供額外的巴士,以應付額外的負荷。這種靈活性允許系統按需擴充套件和收縮。
二、拓撲結構
很好,如果你認可為事件驅動架構所進行的投資。那麼你已經準備好開始建造你的新平臺了。現在,您需要決定架構的整體拓撲結構。有兩種流行的方案。
中介拓撲結構
如圖1所示,中介拓撲結構包括:
● 事件。
● 處理所有事件的事件佇列。
● 一箇中介,負責管理處理事件的順序和渠道。
● 執行業務邏輯的服務。
這是一個抽象的模型,所以,使用何種技術型別的訊息佇列,以及如何實現中介(也稱為控制器或排程器)都取決於你自己。我們團隊用GCP(谷歌雲平臺)Pub/Sub作為訊息佇列技術的選型,然後在Kubernetes引擎和Google計算引擎上執行Node.js微服務作為控制器。這樣我們有N個服務來處理資料並滿足面向物件的“單一責任原則”(面向物件五個基本原則SOLID之一,也是微服務的基本原則之一,即一個模組只做一件事)。
用校車系統的比喻來說,假設一輛巴士需要更換剎車和機油,那麼中介將把巴士交付給替換剎車的機械師。一旦機械師完成,她將通知中介,然後由中介將巴士交給負責更換機油的技工。
代理拓撲結構
代理拓撲結構,如圖2所示,不是利用集中的中介來編排事件和服務。服務向通道訂閱訊息(接受任務指令),執行它們的業務邏輯,然後釋出新訊息(表明任務完成),並由其他服務訂閱(新的服務接受新的任務指令)。這種方法的一個優點是,通過消除對中介的需求,降低了複雜性。缺點是對執行命令的順序沒有進行協調和和處理。當然,這種模式也是技術無關的。
應用到同一個校車系統的比喻中,如果一輛巴士必須更換剎車,然後更換機油,在代理拓撲結構中,巴士就會放到停車場裡,這時,負責換剎車的機械師會收到訊息,從而把車輛拉到自己的車間更換剎車,一旦工作完成,他就會把它還放在停車場裡,接下來,換油的技師會收到換油卡的訊息,並把車停拉到自己的車間裡換機油。這個比喻可能有點牽強,但大意如此。
三、效能
對於EDA來說,吞吐量和延遲是兩個重要的效能指標。延遲越大,吞吐量就越低。有兩個選項可以持續提高系統的效能:通過優化程式碼或配置來減少延遲,或者通過新增額外的資源來提高吞吐量。
在度量效能時,我建議您的團隊為平臺中的每個服務定義服務水平目標(Service Level Objects,SLO)和服務水平指示器(Service Level Indicator,SLI)。我們採用了這種方法,它不僅允許我們監視和評估我們在生產環境中的成功之處,而且它為我們在釋出新特性之前分析基準和效能測試結果提供了一個指南。
我強烈推薦谷歌出版《網站可靠性工程》(Site Reliability Engineering)這本書,如果你還沒有讀過的話。
我從構建可伸縮平臺中學到的最重要的一件事,那就是每毫秒都很重要。1ms的延遲在低資料量的情況下也許無關緊要,但在處理百萬條訊息時,每條訊息增加1ms的延遲,處理時間就增加了15分鐘,如果是10億條訊息的話,處理時間就會增加277個小時。
每毫秒都很重要。在10億條訊息中增加1毫秒的處理時間將使處理時間增加277個小時。
以下是根據我的經驗提出的一些建議:
● 在負載上包含哪些資料,要多動腦筋。
● 為負載上包含的資料量新增約束,以控制性能和費用。
● 永遠不要使用事務性資料庫作為佇列,尤其是在處理可能的大資料量時。像SQL這樣的資料庫必須將每個事務寫入事務日誌。等待寫入事務日誌的時間延遲會影響效能。此外,從同一個表中讀取和寫入將100%導致鎖表和IO等待,從而大大增加延遲。
● 事件驅動系統中有兩個重要日期:實際事件日期和處理日期。實際事件日期是使用者或系統操作發生的時間。處理日期通常是系統攝取事件的時間。區別這一點很重要,因為如果要在特定的時間視窗中執行任何型別的邏輯,從架構層面應該管理訊息的延遲。
解釋下最後一個要點,想象一下學校校長被要求清點當天到達學校的所有學生。她需要花20分鐘等待所有巴士到達,最後有620名學生抵達學校。至此她就不再數數了。用事件驅動的語言來說,就是時間窗戶隨之關閉了。但假設因為一個紅綠燈壞了,有一輛巴士晚了十分鐘。校長將不得不回去,並將額外的80名學生新增到她原來的統計數字中。在您的系統中也必須進行類似的協調。
四、錘子綜合徵
“當你拿著一個錘子,所有問題看起來都像釘子。”
本質上,事件驅動架構並不是適用所有應用程式的正確模式。恰恰相反,這種模式本身就帶來了複雜性。
不要成為錘子綜合症的受害者。
如果你本來只需要一把螺絲刀時,EDAS很可能是解決你問題的錘子。在決定這是否是正確的解決方案時,要考慮開發和維護的成本。
在考慮解決方案所需的複雜性以及是否值得進行投資時,請確定已經瞭解如何處理以下問題:
1、服務抽象的正確級別是什麼?
2、您的事件訊息是schema還是schema-less的?
譯註:
在SQL環境下,schema就是資料庫物件的集合,所謂的資料庫物件也就是常說的表,索引,檢視,儲存過程等。
簡單來說,在schemaless資料的基本單位被命名為單元(cell)。它是不可變的,一旦寫入,便無法被覆蓋。(在特殊情況下,我們可以刪除舊記錄);單元可以被行鍵(row key)、列名(column name)和引用鍵(ref key)來引用;單元內容通過編寫引用鍵更高的新版來執行更新,但行鍵和列名保持不變。Schemaless不對其中儲存的資料執行任何操作,故而命名Schemaless。
3、您的系統如何處理由壞的或有衝突的資料以及服務或佇列的降級所造成的故障?
4、如何處理鄰域噪聲問題?
譯註:
雲是一個多租戶環境,這意味著一個單一的體系結構承載了多個客戶的應用程式和資料。當應用程式或虛擬機器使用大多數可用資源並給共享基礎設施上的其他人造成網路效能問題時,就會產生鄰域噪聲問題。
5、您將如何除錯和理解系統中的事件流?
6、如何處理非冪等事件?
譯註:
一個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。
7、您的系統將如何處理迴圈預防和檢測?
8、您的系統將如何處理分散式事務的回滾?
五、總結
總而言之,EDAS是:
● 高度可伸縮和去中心化的。
● 可以在攝取事件的同時處理事件,並執行聚合等功能。
● 消除點對點整合。
在下列情況下考慮使用EDA:
● 需要低延遲和大資料量處理,
● 您需要在時間視窗內實時地聚合或處理資料,
● 多個服務需要處理同一個事件,
● 您需要水平擴充套件分散式系統,並且
● 您希望實現一個微服務架構。
原文釋出時間為:2018-11-20
本文作者:xxx
本文來自雲棲社群合作伙伴“x”,瞭解相關資訊可以關注“x”。