DDD福音:Zeebe是一個類似Kafka的可擴充套件的分散式事件溯源工作流引擎
許多人認為工作流自動化僅用於人工任務管理等慢速和低頻用例,這體現了當前工作流技術在可擴充套件性方面的侷限性,傳統工作流引擎基於關聯式資料庫,因此它們自然會受到資料庫處理的限制,即使這對大多數公司來說已經足夠了,但是肯定有一些有趣的用例需要更高的效能和可擴充套件性,例如處理需要在非常高的負載下進行軟實時保證的金融交易。
Zeebe是一個沒有任何中心元件的真正的分散式系統,利用Raft Consensus Algorithm等概念實現可擴充套件性和彈性。Zeebe使用事件溯源和事件流概念以及複製的僅附加日誌,分割槽允許擴充套件。根據反應性宣言,它被設計為反應Reactive系統。
Zeebe與Apache Kafka屬於同一類系統,每秒同樣能夠處理相當巨大的事件數量,快於 Camunda 7.8幾百倍。
那麼怎麼能實現這個呢?一個重要的想法是構建一個事件溯源系統。
事件源工作流引擎
傳統工作流引擎捕獲資料庫表中工作流例項的當前狀態。如果狀態更改,則更新資料庫表。使用這種方法,工作流引擎可以利用關聯式資料庫(RDMS)的許多保證,例如ACID事務。
Zeebe的工作方式非常不同,並利用事件溯源。這意味著對工作流狀態的所有更改都將作為事件捕獲,並且這些事件與命令一起儲存在事件日誌中。兩者都被認為是記錄在日誌中。
DDD愛好者的快速提示:這些事件是在Zeebe內部,與工作流狀態有關。如果您在領域中執行自己的事件溯源系統,則通常會為儲存自己的域事件。
記錄是不可變的,因此日誌僅附加。一旦寫完,就不會有任何改變,就像會計雜誌一樣。可以非常有效地處理和擴充套件僅附加日誌。
工作流的當前狀態始終可以從這些事件中派生。這被稱為Project投影或投射。Zeebe中的投影在內部利用RocksDB儲存為快照,RocksDB是一個非常快速的鍵值儲存。RocksDB允許Zeebe在內部透過鍵查詢某些物件,因為純日誌甚至不允許簡單的查詢。
Zeebe 將日誌儲存在磁碟上。目前,這是唯一受支援的儲存選項(其他選項,例如Apache Cassandra會定期討論,但目前尚未在路線圖上討論)。RocksDB還將快照狀態重新整理到磁碟,這不僅建立了更快的啟動時間,而且還允許Zeebe從日誌中刪除已處理的記錄,使其保持相當緊湊。
為了實現效能,彈性和可伸縮性,我們應用了以下分散式計算概念:
Zeebe架構和用法示例
Zeebe在Java虛擬機器(JVM)上作為自己的程式執行。關於執行工作流引擎的體系結構選項,這是遠端引擎方法,因為使用Zeebe的應用程式與它遠端對話。但是,當我們利用流式傳輸到客戶端並使用二進位制通訊協議時,這非常有效且高效。它的巨大優勢在於代理具有已定義的設定和環境,並且不受應用程式程式碼的影響。因此,這個設計決策提供了適當的隔離,我們在支援工作流引擎的多年經驗中瞭解到它的重要性。
視覺化工作流程
Zeebe使用ISO標準BPMN中的視覺化工作流定義,可以使用免費的Zeebe Modeler以圖形方式對其進行建模。
如果您願意,也可以使用YAML來描述工作流程,例如:
name: order tasks: - id: retrieve-payment type: retrieve-payment-service - id: fetch-goods type: fetch-goods-service - id: ship-goods type: ship-goods-service |
支援反應式程式設計,流媒體和背壓的本地語言客戶端
工作流程可以包括所謂的服務任務。當例項到達這些任務時,您需要執行一些程式碼。這是透過建立JobWorkers在您的應用程式中提取的作業來完成的。Zeebe提供本地語言客戶端,例如Java:
ZeebeClient client = ZeebeClient.newClientBuilder().brokerContactPoint("127.0.0.1:26500").build(); JobWorker workerRegistration = client .newWorker() .jobType("my-service-task") .handler(new JobHandler() { public void handle(JobClient client, ActivatedJob job) { // here: business logic that is executed with every job System.out.println(job); // and let the workflow engine know we are done. // The API can be used blocking or non-blocking client.newCompleteCommand(job.getKey()).send().join(); } ) .timeout(Duration.ofSeconds(10)) .open(); |
正如您可能在程式碼中發現的那樣,您可以在應用程式中使用反應式程式設計模型。
您可以根據需要將盡可能多的客戶端連線到Zeebe,並且將分發作業(目前採用迴圈方式),從而實現工作人員的靈活可擴充套件性(上下)。Zeebe很快將支援背壓,因此確保僅以客戶可以處理的速率提供工作。沒有客戶可以被工作所淹沒。如果有疑問,在新客戶連線之前,作業將儲存在Zeebe中。
客戶端是競爭消費者,這意味著一個作業將只由一個客戶執行。這是使用鎖定事件實現的,需要在執行作業之前將其寫入Zeebe。只有一個客戶端可以編寫該鎖定事件,其他嘗試這樣做的客戶端會收到錯誤訊息。鎖定在保持一段時間之後被自動刪除,因為Zeebe認定客戶端在這種情況下已經意外死亡。
事務和至少一次語義
值得注意的是,Zebee客戶端不實現任何形式的ACID事務協議。這意味著在發生故障的情況下,不會回滾任何事務。透過此設定,您有兩種設計選擇:
- 您將事務提交到您的域,然後通知Zeebe完成該作業。現在你的應用程式可能會在提交和完成之間崩潰。因此Zeebe不會知道作業已完成並在鎖定超時後將其交給另一個客戶端。該工作將再次執行。語義是“ 至少一次 ”。
- 您首先完成工作,然後提交您的交易。如果應用程式崩潰,您可能已完成作業但未提交事務。工作流程將繼續進行。語義是“ 至多一次 ”。
大多數時候你會決定使用“最多一次”,因為它在大多數用例中最有意義。
由於您的程式碼可能被多次呼叫,因此您必須使應用程式邏輯具有冪等性。這在您的域中可能很自然,或者您可能會想到其他策略並建立一個冪等接收器(請參閱例如Spring Integration)。我在微服務整合的三個常見陷阱中簡要地解決了冪等性- 以及如何避免它們並計劃一篇關於它的擴充套件文章。
透過CQRS查詢
Zeebe代理負責執行正在執行的工作流程。它被最佳化為將新命令應用於當前狀態,以達到開頭提到的效能和可伸縮性目標。但是,Zeebe代理無法提供任何查詢,例如“今天早上在8到9之間啟動了哪些工作流例項但尚未完成?”。由於我們不再使用關聯式資料庫,因此無法執行簡單的SELECT語句。在這種情況下,我們確實需要一種不同的方式來處理所謂的查詢模型。
這種分離命令和查詢模型的方式稱為命令查詢責任分離(CQRS),具有很大的優勢:
CQRS允許您將負載與讀取和寫入分開,允許您獨立地進行擴充套件。[...]您可以向雙方應用不同的最佳化策略。一個例子是使用不同的資料庫訪問技術進行讀取和更新。
這正是我們對Zeebe所做的。Broker利用事件流和最佳化高吞吐量和低延遲。但它不提供查詢功能。這就是為什麼Zeebe提供所謂的Exporters,它可以訪問整個事件流。一個開箱即用的Exporters是Elasticsearch。透過使用它,所有事件都寫入Elastic並儲存在那裡,隨時可以供您查詢。
Zeebe現在提供了一個操作工具,您可以使用它來檢視工作流引擎:Operate.。您可以看到正在發生的事情,識別問題(所謂的事件)以及根源和修復事件。
Operate也可以擴充套件並在Elasticsearch上使用自己的最佳化索引:
原始碼可用的許可證
您可以下載,修改和重新分發Zeebe程式碼。您可以將Zeebe包含在商業產品和服務中。只要您不提供通用工作流程服務,類似雲提供商那樣。
總結
Zeebe被設計為一個真正可擴充套件且具有彈性的系統,沒有中央資料庫。它非常高效。它可以與幾乎任何程式語言一起使用。它使用BPMN中的視覺化工作流,允許真正的BizDevOps。這種組合使它與我所知道的任何編排或工作流引擎區別開來。
相關文章
- uber/cadence:Cadence是一種分散式,可擴充套件,持久且高度可用的流程編排引擎分散式套件
- 如何使用Zebee構建高度可擴充套件的分散式工作流中介軟體?套件分散式
- Zeebe與Kafka的天作之合:將Zeebe工作流引擎引入Apache Kafka實現微服務編排 - Bernd RückerKafkaApache微服務
- PowerToys外掛擴充套件(類似Alfred)套件Alfred
- 可擴充套件Web架構與分散式系統套件Web架構分散式
- 讀構建可擴充套件分散式系統:方法與實踐15可擴充套件系統的基本要素套件分散式
- DoorDash使用 Kafka 和 Flink 構建可擴充套件的實時事件處理Kafka套件事件
- dubbo是如何實現可擴充套件的?套件
- MemQ:可替代Kafka的高效、可擴充套件的雲原生PubSub系統MQKafka套件
- WPF如何封裝一個可擴充套件的Window封裝套件
- 可擴充套件的TextView,ExpandableTextView與Scroller類的使用套件TextView
- 讀構建可擴充套件分散式系統:方法與實踐09可擴充套件資料庫基礎套件分散式資料庫
- dubbo是如何實現可擴充套件的?(二)套件
- PHP擴充套件開發就是一個自己的PHP擴充套件PHP套件
- Slack是如何實現分散式任務處理的擴充套件?分散式套件
- 一個可擴充套件的報警系統Quick-Alarm套件UI
- iOS一個靈活可擴充套件的開源Log庫iOS套件
- 如何開始一個模組化可擴充套件的Web App套件WebAPP
- INFORMIX表的預設初始擴充套件、下一個擴充套件資料塊以及一個表允許的最大擴充套件數。ORM套件
- 可擴充套件性套件
- Kotlin的幾個擴充套件函式Kotlin套件函式
- 可擴充套件性筆記一套件筆記
- 可擴充套件的搜尋元件套件元件
- 構建可擴充套件的應用(一) (轉)套件
- gmcache一個用go寫的分散式快取,類似memcachedGo分散式快取
- 我的第一個Emacs擴充套件Mac套件
- Jenkins2.65釋出,可擴充套件的持續整合引擎Jenkins套件
- kotlin 擴充套件(擴充套件函式和擴充套件屬性)Kotlin套件函式
- Laravel-permission(一個許可權管理的擴充套件包) 的使用Laravel套件
- 分類擴充套件套件
- .NET分散式Orleans - 6 - 事件溯源分散式事件
- weex ios擴充套件類的作用iOS套件
- 基於Apache Spark以BigDL搭建可擴充套件的分散式深度學習框架ApacheSpark套件分散式深度學習框架
- MySql 擴充套件儲存引擎MySql套件儲存引擎
- MySQL中InnoDB引擎對索引的擴充套件MySql索引套件
- 分散式、高吞吐量、高可擴充套件性訊息佇列服務Kafka商業化釋出!分散式套件佇列Kafka
- 五個檢視擴充套件類 LL套件
- Kafka分散式查詢引擎Kafka分散式