微服務中的事件、流程和長時間執行業務

banq發表於2017-12-25

本文是討論微服務領域事件架構下一些需要長時間執行服務的設計問題,這些長時間執行的服務任務是因為有人工流程介入導致,比如請假需要所在部門和人事部門等兩個部門領導批准,那麼請假這個服務就可能需要一兩天時間才能完成,因為需要兩個部門領導都在電腦前且點按了批准按鈕,這個請假服務流程才結束。

本文關鍵點:

1. 圍繞微服務架構的討論促進了EDA事件驅動架構思想的發展,EDA能有效地解耦服務。領域事件概念屬於EDA一種。

2. 領域事件非常適合分散的資料管理,能夠根據寫模型產生用於讀取的資料模型(CQRS讀寫分離)或解決業務上很多交叉耦合問題。 但是,您不應該執行復雜的點對點事件鏈。可以使用“命令”這個概念來協調其他服務能夠進一步減少耦合。

3.集中管理的ESB不適合微服務架構。微服務中推薦智慧端點和啞管模型。但是,不要因為害怕引入中央控制而放棄服務的協調:重要的業務能力需要一個總控點。

4.過去,BPM和工作流引擎在戰略方向上有問題,所以市場上出現許多可怕的所謂“零程式碼”的視覺化流程開發工具卻嚇跑了開發者。現在出現了輕量級且易於使用的流程框架了,其中有許多框架是開源的。 不要花時間編寫自己的流程狀態機,而是要利用現有的工作流工具。這將幫助您避免意外的複雜性。



如果您一直關注最近圍繞微服務架構風格的討論,您可能會聽到這樣的建議:“為了有效地分離微服務,您必須建立一個事件驅動架構”。

這個想法得到了領域驅動設計(DDD)社群的支援,這裡介紹如何使用領域事件以及它們是如何改變我們對系統的看法。

雖然我們普遍支援事件驅動導向,但是如果沒有進一步的思考就會增加使用風險。為了回答這個問題,我們回顧了三個常見的假設:

1. 事件減少耦合
2. 中央控制事件匯流排ESB需要避免
3. 工作流引擎是痛苦的


我們假設我們有四個有界的上下文產生四個專用的服務(可能是微服務,自包含系統或其他形式的服務):

1. Checkout結賬服務
2. Payment支付服務
3. Inventory庫存服務
4. shipment貨運服務


如何使用事件實現解耦

讓我們假設上述Checkout服務在“如果一個物品有貨可以馬上運送”的情況下應該給使用者一個反饋,。Checkout服務可以使用請求/響應向庫存服務詢問庫存數量,但是這將使得Checkout和Inventory耦合起來(就可用性、響應時間等而言)。

另一種方法是,庫存釋出對庫存物品數量的任何變化作為廣播事件,讓其他模組能夠知道這種變化。結帳可以監聽這些變化事件,並將當前庫存量儲存在自己的內部,然後在本地處理相關功能。這些本地資訊顯然是一個副本,可能與庫存中資料並不完全一致。但是,某種程度的最終一致性通常是可以在分散式系統中進行必要的權衡的(CAP定理)。

(第二種辦法就是引入領域事件的事件驅動方式)

另一個使用事件驅動用例是能夠解耦核心與非核心功能,如兩者業務交叉的問題。例如,在您的訂單完成後你想傳送通知給客戶。通知服務以完全自主的方式實施並儲存有關客戶的通知偏好和聯絡人等資料。然後它能夠傳送有關“收到付款”或“發貨”等特定事件的客戶電子郵件,這些無需在其他服務中進行任何更改。這裡如果引入領域事件,將使得傳送通知的非核心功能從訂單服務中解耦出來,事件驅動架構(EDA)能夠讓新增新服務或擴充套件現有服務變得非常簡單。

點對點事件鏈的風險

一旦團隊開始使用事件驅動架構,他們往往會迷戀事件 - 事件提供了驚人的解耦,所以盡情地使用它們!當您透過點對點事件鏈實施端到端的流程(如訂單履行業務流程)時,問題開始出現。我們假設一個相當微不足道的流程。它可以透過事件鏈中的下一個服務總是知道什麼時候需要什麼服務做什麼來實現,事件鏈服務如下:

Checkout ----> Payment -----> Inventory ----->Shipment


上述流程是支付好以後到庫存中取貨,然後發貨,現在,假如在付款Payment之前執行需要先取貨:

Checkout ----> Inventory -----> Payment ----->Shipment


您必須調整和重新部署多個微服務,才能更改業務流程。(原文認為這通常是微服務環境下的反模式,因為這種架構風格的核心設計原則是爭取更少的耦合和更多的服務自主權,但是微服務獨立部署獨立重啟正是微服務的一個重要特點)。因此,我們建議您在使用事件實施點對點事件鏈之前要三思而行,特別是在期待相當複雜的情況下。

使用命令,但不需要中央控制

解決這個問題的一個更明智的方法是在一個專門的Order服務中實施它。該服務可以充當協調員角色,並向其他服務傳送命令,這種方式可以描述為:“Order服務協調編排付款payment、庫存Inventory和貨運Shipmengt等服務以完成客戶的業務需求”。

然而,一旦我們談論協調編排,有些人就會想到“神奇”的企業服務匯流排(ESB)或集中式業務流程建模(BPM)解決方案。這些工具過去存在不好使用的印象,因為這通常意味著你不得不放棄易於測試或部署自動化的專有工具。所以,James Lewis和Martin Fowler建議在微服務中使用“ 智慧終端和啞管道 ” 。

但是,上面引入協調者的設計並沒有提出一個聰明管道(啞管的反模式)。編配服務處理作為一流公民執行訂單實現,並在一個專門服務order中執行它。這樣的服務可以用任何你喜歡的方式,用你喜歡的任何技術堆疊來實現。現在只是你有一個專門的地方,讓你可以瞭解流程,並透過改變一個服務改變流程。

Sam Newman在“建立微服務”一書中描述了另一種風險,就是這種編排訂單服務- 隨著時間的推移,這會發展成吸納了所有業務邏輯的“上帝服務”,而其他服務則降級為“貧血”服務,或者變得更糟CRUD式的“實體”服務。這是否會在這裡發生呢?不,讓我們快速再次回顧Martin Fowler的“智慧終端”概念。

什麼是智慧終端?這是其實是一種關於良好的API設計。對於支付服務,您可以設計一個有效的粗粒度API,它可以接受“請求支付”命令,併發出“已收到付款”或“付款失敗”事件(這裡有一個模式:命令 -->服務 --->事件,命令是服務的輸入,事件是服務的輸出)。支付服務中其實內建了複雜的業務邏輯處理比如客戶信用卡驗證等。在這種情況下,支付服務不會因為它在某種其他環境中被編排(或者更簡單地說,“使用”)而變成貧血。


長時間執行的服務

為了設計智慧終端併為您的客戶提供有價值的API,您必須認識到許多服務可能會長期執行,因為他們需要解決幕後的業務問題(人工稽核介入)。假設在信用卡到期的情況下,我們會給客戶一個更新修改的機會,如果將該職責功能保留在付款Payment服務內,等待客戶提供新的信用卡資訊意味著付款仍然可能被請求失敗。如果payment支付服務不關心這個客戶更新信用卡更新的功能,則會將該職責推給Order服務。Payment支付服務則會更清潔,更符合有界上下文DDD想法。。支付API變得非常清晰,而且服務易於使用。但是,在某些情況下,對於客戶更新信用卡資訊,我們可能需要兩週的時間才能獲得其業務響應。這就是我們所說的“長時間執行”的業務流程。

長時間執行的服務需要以某種方式持久儲存狀態。這裡如果還沒有完成付款,那麼訂單還沒有完成,並處於等待該付款的狀態。這些狀態必須在系統重新啟動之後還能夠獲得。當然,處理持久狀態不是一個新問題,有兩個典型的解決方案:

1. 實現你自己的永續性“實體”,如資料庫實體,永續性框架等,問問自己,你是否曾經建立過一個名為status狀態的列表?

2.你在這利用狀態機或工作流引擎。
市場上有很多工具和框架,其中一些已經非常成熟。最近在這個領域還有一些創新,例如NetflixUber開發自己的開源專案。


根據我們自己的經驗,實現您自己的狀態處理持久機制往往會導致本地的狀態機。往往面臨後續的需求,例如超時處理(“嘿,讓我們給遊戲新增一個排程器”),狀態可見性和報告(“為什麼業務人員不能用SQL來查詢資訊? “),或者如果出現錯誤(”嗯“)的監視操作。

自己實現狀態機如此常見的原因不僅是因為“Not-Invented-Here”綜合症,還因為圍繞工作流的概念和市場上老式的BPM工具。許多開發人員對通常定位為“零程式碼”的工具感到痛苦。這樣的工具首先被賣給了業務部門,他們往往想要擺脫開發人員,這當然還沒有發生。相反,這些工具後來被移交給IT部門,在那裡仍然處於“外星人”地位。這些工具通常是重量級和專有的,開發人員會遇到我們稱之為“death-by-properties-panel因屬性皮膚而死亡”的問題。

輕量級狀態機和工作流引擎

輕量級和靈活的業務流程引擎確實存在,可以像其他所有庫一樣使用 - 只需很少的程式碼。他們不把自己定位為“零程式碼”,而是作為開發者工具箱中的工具。他們解決了狀態機的難題,許多專案早期就能看到回報。

這樣的工具允許您用ISO標準BPMN圖形化流程,或者通常基於JSON,YAML或語言相關DSL(如Java或Golang)的其他流程語言來定義流程。一個重要的方面是流程定義是有效的原始碼,因為它們將被直接執行。執行意味著狀態機知道如何從一個狀態轉換到另一個狀態。

成熟的流程語言(如BPMN)提供了相當強大的概念,例如處理時間和超時,或複雜的業務事務。因為有很多專案利用BPMN,所以我們知道我們可以用它來解決棘手的需求。

在上面的例子中,工作流例項等待一個Goods Fetched取貨事件,直到某個超時觸發為止。如果發生這種情況,業務交易將得到補償,即所有的補償活動都將被執行,在這種情況下,付款將被退還。狀態機跟蹤已經執行的活動,因此能夠觸發所有必要的補償動作。這實現了狀態機協調業務的事務交易 - 底層設計也被稱為Saga模式。

利用圖形符號來定義這樣的流程還增加了活文件的思想 - 文件與執行系統的排頭並齊,使其不會與實際行為不同步。一些工具為單元測試包括長時間執行的特定情況提供了特殊的支援。例如在Camunda中,每個測試執行都會生成一個HTML輸出,該輸出突出顯示已執行的場景,這些內容可以輕鬆掛接到正常的持續整合(CI)報告中。這樣的圖形模型增加了更多的價值:


工作流程位於服務邊界內部

一個非常重要的概念是使用業務工作流框架和工具是每個服務團隊做出的分散決策。狀態機應該是一個在服務外部不可見的實現細節。不需要任何中央工作流程工具,而且狀態機應該只是一個庫,用來更容易地使你的一些服務的長期執行行為。


另一種看待這個問題的方式是這樣一個工作流引擎是你的服務的邏輯部分。根據您選擇的工具,它可以使用簡單的語言客戶端(例如使用Java或Go和Zeebe)作為獨立的程式執行嵌入到您的應用程式過程(例如,使用Java,Spring和Camunda),也可以由REST API (例如使用Camunda或Netflix指揮)。擁有這種基礎設施可以使服務免受實施狀態處理本身的負擔,以專注於業務邏輯。您可以設計出良好的服務API和真正的智慧端點,因為您可以輕鬆決定讓服務長期執行。


我們已經使用Java開源元件(Spring Boot,Camunda,Apache Kafka)開發了這樣案例:按這裡

原文:

Events, Flows and Long-Running Services: A Modern

相關文章