[译] 基于事件流构建的服务
[譯] 基于事件流構(gòu)建的服務(wù)
摘要:本文屬于原創(chuàng),歡迎轉(zhuǎn)載,轉(zhuǎn)載請保留出處:https://github.com/jasonGeng88/blog
原文:https://www.confluent.io/blog/build-services-backbone-events/
對許多人來說,微服務(wù)是建立在請求和響應(yīng)協(xié)議之上的,如 REST 等等。這種方法很自然。 我們編寫程序是一回事,我們調(diào)用其他代碼模塊,等待響應(yīng)并繼續(xù)。它也與我們每天看到的大量使用情況緊密相連:前面的用戶點擊按鈕的網(wǎng)站,并期待事情發(fā)生。
但是當(dāng)我們進(jìn)入許多獨(dú)立服務(wù)的世界時,事情就會開始變化。隨著服務(wù)的數(shù)量隨著時間的推移逐漸增長,同步交互的網(wǎng)絡(luò)也隨之增長。以前良性的可用性問題開始引發(fā)更廣泛的中斷。
在分布式系統(tǒng)中,排查問題對于我們不幸的運(yùn)維工程師來說,將是艱巨的任務(wù)。瘋狂的從一個服務(wù)到另一個服務(wù),拼湊各個服務(wù)的信息片段。
這是一個眾所周知的問題,并且有一些解決方案。一個是確保您的個人服務(wù)具有比您的系統(tǒng)更高的 SLAs。Google 提供了這樣做的協(xié)議。另一種方法是簡單地分解將服務(wù)綁定在一起的同步關(guān)系。
我們可以使用異步作為這樣做的機(jī)制。如果你在在線零售工作,你會發(fā)現(xiàn)同步接口,如 getImage() 或 processOrder() 感覺自然,期望調(diào)用得到立即響應(yīng)。但是當(dāng)用戶點擊“購買”時,它會觸發(fā)一個復(fù)雜而異步的過程。一個采購的過程,并將其線下交付到用戶門口,這種方式已經(jīng)超出原本按鈕的上下文。因此,將軟件分解成異步流可以使我們能夠區(qū)分我們需要解決的不同問題,并允許我們擁抱一個本身就是異步的世界。
在實踐中,我們發(fā)現(xiàn)會有輪詢數(shù)據(jù)庫表來進(jìn)行更改,或者通過一些定時任務(wù)來進(jìn)行更新。這些是打破同步關(guān)系的簡單方式,但這些方式給人不透明的感覺,像有黑客篡改了你的數(shù)據(jù)一樣。可能他們有一個很好的理由。
所以我們可以將所有這些問題集中到一個觀察中。我們命令服務(wù)去做我們要求的事情,這樣的命令式編程模式不適合獨(dú)立運(yùn)行的服務(wù)。
在這篇文章中,我們將看看架構(gòu)硬幣的另一面:不是通過命令鏈,而是通過事件流的方式來組合服務(wù)。這是一個有效的方法。它也為我們將在本系列后面討論的更先進(jìn)的模式形成基準(zhǔn),我們將事件驅(qū)動處理的想法與流式平臺中的觀點相結(jié)合。
命令(Commands)、事件(Events)和查詢(Queries)
在我們深入研究一個例子之前,我們需要解決三個簡單的概念。服務(wù)可以通過三種方式相互交互:命令、事件和查詢。 如果你以前沒有考慮過這三者之間的區(qū)別,那么這很值得。
事件的優(yōu)點是事實和觸發(fā)。外部數(shù)據(jù)可以被系統(tǒng)中的任何服務(wù)來重用。但從服務(wù)的角度來看,事件造成的耦合又要比命令和查詢來得低。這個事實很重要。
服務(wù)間交互的三種方式:
命令:是一個動作,在另一個服務(wù)中執(zhí)行某些操作的請求。有些會改變系統(tǒng)狀態(tài)。命令會期待一個響應(yīng)結(jié)果。
事件:既是事實也是觸發(fā)器。對已經(jīng)發(fā)生事情的一種通知。
查詢:是查找某物的一個請求。重要的是,查詢是無副作用的,不會造成系統(tǒng)狀態(tài)的改變。
一個簡單的事件驅(qū)動流程
我們從一個簡單的例子開始:客戶訂購小部件,這個行為會觸發(fā)接下來的兩個事情:
處理相關(guān)的付款。
系統(tǒng)檢查以查看是否需要更多小部件。
在請求驅(qū)動的方法中,這可以表示為一連串的命令。目前沒有查詢。互動將如下所示:
要注意的第一個事情是通過調(diào)用訂單服務(wù)初始化“購買更多的庫存”的業(yè)務(wù)流程。這混合了兩個服務(wù)的責(zé)任,理想情況,我們應(yīng)該更好的分離關(guān)注點。
現(xiàn)在,如果我們可以使用一個事件驅(qū)動方法來代表相同的流程,那么事情會變得更好。
UI 服務(wù)觸發(fā)一個“訂單請求”事件,并在返回給用戶之前等待一個“訂單確定”(或拒絕)的事件。
訂單服務(wù)與庫存服務(wù)都通過觸發(fā)的事情來進(jìn)行響應(yīng)。
仔細(xì)看,UI 服務(wù)與訂單服務(wù)的交互沒有任何改變,除了他們是通過事件來交流的,而不是直接調(diào)用對方。
庫存服務(wù)很有趣。訂單服務(wù)不再告訴它要做什么,也不再控制是否參與互動。這是這種類型的架構(gòu)非常重要的屬性,稱為 “Receiver Driven Flow Control”。邏輯被推送到事件的接收者,而不是發(fā)送者。責(zé)任的重任進(jìn)行了翻轉(zhuǎn)!
將控制轉(zhuǎn)移到接收者可減少服務(wù)之間的耦合,從而為架構(gòu)提供了重要的可插拔性。組件可以輕松地?fù)Q入換出。
隨著架構(gòu)變得越來越復(fù)雜,可插拔性的這一要素變得越來越重要。說我們要添加一個實時管理定價的服務(wù),根據(jù)供需調(diào)整產(chǎn)品的價格。在一個命令驅(qū)動的世界里,我們需要引入一個可以由庫存服務(wù)和訂單服務(wù)調(diào)用的 maybeUpdatePrice() 方法。但在事件驅(qū)動的世界重新定價只是一種訂閱共享流的一個服務(wù),當(dāng)滿足相關(guān)標(biāo)準(zhǔn)時發(fā)送價格更新。
混合事件與查詢
上面的例子只考慮了命令/事件。 沒有查詢操作(請記住我們將所有交互定義為命令、事件和查詢的其中一種)。查詢是除了最簡單的架構(gòu)之外的所有架構(gòu)的必需品。所以我們來擴(kuò)展這個例子,讓訂單服務(wù)檢查在處理付款之前有足夠的庫存。
對此的請求驅(qū)動方法將涉及向庫存服務(wù)發(fā)送查詢以檢索當(dāng)前庫存數(shù)量。這導(dǎo)致混合模型,其中事件流純粹用于通知,允許任何服務(wù)進(jìn)入流程,但查詢直接轉(zhuǎn)到源。
對于服務(wù)需要獨(dú)立發(fā)展的更大的生態(tài)系統(tǒng),遠(yuǎn)程查詢增加了很多耦合,在運(yùn)行時將服務(wù)捆綁在一起。 我們可以通過內(nèi)部化來避免這種跨上下文的查詢。事件流用于緩存每個服務(wù)中的數(shù)據(jù)集,使其可以在本地進(jìn)行查詢。
所以要添加這個庫存檢查,訂單服務(wù)將訂閱庫存事件流,將它們進(jìn)行本地存儲。然后直接查詢本地緩存來驗證是否有足夠的庫存。
純事件驅(qū)動系統(tǒng)沒有遠(yuǎn)程查詢的概念 - 事件將狀態(tài)傳播到在本地進(jìn)行查詢的服務(wù)
這種“按事件傳播查詢”方法有三個優(yōu)點:
更好的解耦:查詢是本地的。它們不涉及跨上下文調(diào)用。這種服務(wù)的耦合性遠(yuǎn)遠(yuǎn)低于他們請求驅(qū)動時的耦合性。
更好的自治:訂單服務(wù)具有庫存數(shù)據(jù)集的私有副本,因此它可以隨意使用它,而不僅限于庫存服務(wù)提供的查詢功能。
高效連接:如果我們在每個訂單上“查詢庫存”,我們將有效地通過兩個服務(wù)之間的網(wǎng)絡(luò)進(jìn)行連接。隨著負(fù)載的增加,或者更多的資源被使用時,這可能會變得非常糟糕。按事件傳播查詢通過將查詢(和連接)本地化來解決此問題。
這種做法不是沒有缺點。服務(wù)內(nèi)部變成有狀態(tài)的。他們需要跟蹤和處理一段時間內(nèi)傳播的數(shù)據(jù)集。狀態(tài)的重復(fù)也可能使一些問題更難理解(我們?nèi)绾卧拥販p少庫存數(shù)量?),我們也應(yīng)該注意數(shù)據(jù)是最終一致性的問題。但是,所有這些問題都有可行的解決方案,他們只需要考慮一下。
單獨(dú)寫原則
適用于這種風(fēng)格的系統(tǒng)的有用原則是將特定類型的傳播事件的責(zé)任分配給單個服務(wù):單獨(dú)寫原則。因此,庫存服務(wù)部門將擁有“庫存清單”如何隨時間推進(jìn),訂單服務(wù)部門將擁有訂單等。
這有助于通過單個代碼路徑(盡管不一定是單個進(jìn)程)來保證一致性,驗證和其他“寫入路徑”問題。所以,在下面的示例中,請注意,訂單服務(wù)控制對訂單進(jìn)行的每個狀態(tài)更改,但整個事件流量跨越訂單、付款和發(fā)貨,每個由其各自的服務(wù)管理。
分配事件傳播的責(zé)任很重要,因為這些不僅僅是短暫的事件,也不是短暫的聊天。他們代表共同的事實,數(shù)據(jù)在外面。因此,隨著時間的推移,服務(wù)需要負(fù)責(zé)策劃這些共享數(shù)據(jù)集:修復(fù)錯誤,處理模式變化等情況。
這里每個顏色代表 Kafka 在訂單、發(fā)貨和付款中的一個主題(topic)。當(dāng)用戶點擊“購買”時,會觸發(fā)“訂單請求”,等待“訂單確認(rèn)”事件,然后再回復(fù)給用戶。另外三個服務(wù)處理與其工作流程部分相關(guān)的狀態(tài)轉(zhuǎn)換。例如,付款處理完成后,訂單服務(wù)將訂單從驗證推送到已確認(rèn)。
混合模式和集群服務(wù)
對于上面描述的一些模式看起來像企業(yè)消息(Enterprise Messaging),但還是有些許的不同。企業(yè)消息在實踐中側(cè)重于狀態(tài)的轉(zhuǎn)移,通過網(wǎng)絡(luò)有效地將數(shù)據(jù)庫捆綁在一起。
事件協(xié)作是關(guān)于服務(wù)通過一系列事件來處理一些業(yè)務(wù)目標(biāo),這些事件將觸發(fā)服務(wù)的行動。所以這是業(yè)務(wù)處理的一種模式,而不是簡單的移動狀態(tài)的機(jī)制。
但是,我們通常希望在我們構(gòu)建的系統(tǒng)中利用這種模式的“faces”。事實上,這種模式的美妙之處在于它可以處理微觀和宏觀,或者在有意義的情況下被混合。
組合模式也很常見。 我們可能想要遠(yuǎn)程查詢的靈活性,而不是本地維護(hù)數(shù)據(jù)集的開銷,特別是在數(shù)據(jù)集增長時。這使部署簡單的功能變得容易(如果我們要組合輕量級、無服務(wù)器架構(gòu)和事件流,這一點就很重要),或者因為我們處于無狀態(tài)的容器或瀏覽器。
訣竅是限制這些查詢接口的范圍,理想情況下是在界限上下文中。通常情況下,具有多個特定目標(biāo)視圖的架構(gòu)會比單一的共享數(shù)據(jù)存儲的架構(gòu)要好。(界限上下文,這里是一組共享相同的部署周期或領(lǐng)域模型的服務(wù)。)
為了限制遠(yuǎn)程查詢的范圍,我們可以使用集群上下文模式。這里事件流是上下文之間的唯一溝通模式。但是,上下文中的服務(wù)會利用他們所需的事件驅(qū)動處理和請求驅(qū)動視圖。
在下面的例子中,我們有三個部分,只通過事件相互溝通。 在每一個內(nèi)部,我們使用更細(xì)粒度的事件驅(qū)動流。 其中一些包括視圖層(查詢層)。這平衡了耦合的便利性,允許我們將細(xì)粒度的服務(wù)與較大的實體相結(jié)合; 傳統(tǒng)的應(yīng)用程序或現(xiàn)成的產(chǎn)品中都存在許多真實的服務(wù)場景。
集群上下文模式
事件驅(qū)動服務(wù)的五大優(yōu)勢:
解耦:如果基于事件,服務(wù)可以更容易地插入到現(xiàn)有的事件流中,或者重做工作流的一些子集。
離線/異步流:服務(wù)卸下了保證交付給消息協(xié)商器(broker)的責(zé)任。這使得以事件驅(qū)動的方式輕松管理離線任務(wù)。
狀態(tài)轉(zhuǎn)移:事件流提供了一種分發(fā)數(shù)據(jù)集的有效機(jī)制,因此可以在界限上下文中重構(gòu)和查詢。
連接:從不同的服務(wù)組合/加入/擴(kuò)充數(shù)據(jù)集更容易。連接是快速和本地化的。
可追溯性:當(dāng)有一個中心的、不可變的,保持?jǐn)⑹滦缘?#xff0c;每次互動隨著時間的推移而日常化的記錄時,調(diào)試分布式將變得更加容易。
總結(jié)
所以在事件驅(qū)動的方法我們使用事件代替命令,事件觸發(fā)處理。它們也變成我們可以在本地查詢的視圖。我們在必要時回到遠(yuǎn)程同步查詢,特別是在較小的生態(tài)系統(tǒng)中,但是在較大的系統(tǒng)中我們限制了它的范圍(理想情況下,僅限于單個界限上下文)。
但所有這些方法只是模式。一起構(gòu)建系統(tǒng)的指導(dǎo)原則。對于它們,我們不應(yīng)該太教條化。例如,如果它是很少改變的東西(比如單點登錄服務(wù)),一個全局查詢服務(wù)仍然是一個好主意。
訣竅是從事件的基準(zhǔn)開始。事件提供了更少的服務(wù)機(jī)會來將自己相互聯(lián)系起來,并且將流程控制轉(zhuǎn)移到接收者使得更好的分離關(guān)注和更好的可插拔性。
關(guān)于事件驅(qū)動方法的另一個有趣的事情是,它們對于大型、復(fù)雜的架構(gòu),和對于小型、高度協(xié)作的架構(gòu)同樣起到很好的作用。事件的支柱為服務(wù)提供自由發(fā)展所需的自治權(quán)。去除了復(fù)雜的命令和查詢關(guān)系。對于運(yùn)維工程師來說,系統(tǒng)排查仍然是痛苦的,但是希望不是那么頻繁,至少現(xiàn)在這個故事還有一個腳本!
但是,隨著所有這些事件的討論,我們談到了很少的分布式日志或流處理。當(dāng)我們將這種模式與 Kafka 應(yīng)用時,系統(tǒng)的規(guī)則就會改變。協(xié)商器的保留特性成為我們可以設(shè)計的工具,允許我們接觸外部的數(shù)據(jù)。某些服務(wù)可以參考。
流式平臺非常自然地與這種模型的處理事件和構(gòu)建視圖相配。直接嵌入到服務(wù)中的視圖,遠(yuǎn)程服務(wù)查詢的視圖,或視圖實現(xiàn)作為一個持續(xù)的流。
這導(dǎo)致了一系列優(yōu)化:利用事件流和事件存儲之間的對偶性,混合流式處理工具,篩選敘述,加入來自許多服務(wù)的流和實現(xiàn)我們可以查詢的視圖。這些模式正在賦予權(quán)力。它們允許我們使用專門用于處理事件流的工具集來重新構(gòu)想業(yè)務(wù)處理。但是,所有這些優(yōu)化都是基于這里討論的概念,僅僅應(yīng)用于更現(xiàn)代的工具集。
在下一篇文章中,我們將通過考慮將日志的保留屬性作為我們的服務(wù)生態(tài)系統(tǒng)的組成部分來使數(shù)據(jù)更具確定性。我們將數(shù)據(jù)保存在外部作為可以依賴的中央共享敘事的地方。
總結(jié)
以上是生活随笔為你收集整理的[译] 基于事件流构建的服务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: poj 2683 Ohgas#39; F
- 下一篇: apt命令与yum命令