歡迎光臨
每天分享高質量文章

使用gRPC, NATS, CockroachDB構建EventSourcing/CQRS的微服務

導讀:




本文的主要目的是通過EventSourcingCQRS來構建事件驅動的微服務。構建真實世界的微服務是非常複雜的,其中最困難的部分是處理分散在各個微服務擁有的幾個資料庫中的資料。這使得構建跨多個微服務的業務事務變得非常複雜,並且查詢資料時對多個資料庫無法進行關聯查詢。因此,您必須使用一些架構方法從現實世界的角度構建微服務,您可以考慮使用事件驅動的體系結構,如EventSourcingEventSourcing架構最好與CQRS結合使用。雖然本文是基於go語言實現,但代碼簡單清晰,清楚的介紹了EventSourcingCQRS如何結合使用,其它語言的工程師也可閱讀,相信會有幫助。

在這篇文章中,我將演示Go中的一個簡單的EventSourcing/CQRS示例,演示如何解決基於微服務的分佈式系統的實際挑戰。這篇文章的目的不是介紹EventSourcingCQRS的最佳實踐,而是通過在Go中編寫一個簡單的示例來介紹這兩種架構和設計樣式,它為構建基於微服務的分佈式系統提供瞭解決方案,以便處理事務和資料。

    

微服務中最難的部分:資料和事務

從未在分佈式系統上工作的人一直誤解微服務只能在Docker容器中運行服務並使用Kubernetes進行編排, 很多人一直在提供關於微服務的企業培訓和指導,他們有兩個誤解:微服務只是用Kubernetes編排容器,或者只是在Netflix OSS中使用Spring Boot框架。 但實際上,微服務是一種分佈式系統架構樣式,它完全用於構建高度可擴展和可演化的軟體系統的功能分解。 簡而言之,微服務是一種協同工作的小型自治服務。

資料分散在微服務的多個資料庫中



分解微服務的一種常用做法是針對有界背景關係來設計每個微服務,這是領域驅動設計(DDD)中的中心樣式,通過將大的域模型劃分為多個不同的邏輯域,將業務問題分解到各個子域的有界背景關係中。單個有界背景關係可以通過一個或多個聚合進行事務。因為我們通常針對每個有界背景關係構建微服務,所以我們通常為每個微服務使用單獨的資料庫,每個微服務看起來像一個獨立的系統。

因為我們將單體系統分解成幾個自治服務,所以資料會分散在各個微服務所對應的幾個資料庫中。這使您的應用程式和體系結構變得非常複雜。 例如,業務事務可能跨越多個微服務。 假設您構建了一個帶有微服務的電子商務系統,其中下訂單最初將由一個微服務——OrderService處理,然後支付處理可能由另一個服務——PaymentService完成,依此類推。 另一個挑戰是從多個資料庫查詢資料。 使用單體(monolithic)資料庫,您可以輕鬆地從資料庫中執行連接查詢。 在微服務中,由於單體資料庫作為功能組件被分解到多個資料庫中,因此不能簡單地執行連接查詢,必須要從多個資料庫中獲取資料。

    

使用EventSourcingCQRS構建微服務

為瞭解決微服務的實際挑戰,事件驅動的、反應式的系統在領域驅動設計中結合使用是一個很好的辦法。因此,我強烈建議使用EventSourcing,這是以事件為中心的架構,通過編寫各種事件來構建應用程式狀態。


EventSourcing: 使用不可變日誌記錄事件變化的儲存



EventSourcing通過不可變的日誌儲存事件,其中每條日誌(代表對某個物件的更改)表示應用程式的狀態。事件儲存就像版本控制系統,在微服務架構中,我們可以將聚合操作通過一系列的事件進行持久化儲存,事件就是事實,代表系統中發生的一些行為。這些是不可變的,無法更改或撤銷的。電子商務系統中的事件包括OrderCreatedPaymentDebitedOrderApprovedOrderRejectedOrderShippedOrderDelivered等。

在您的EventSourcing架構中,當您從一個微服務中發佈一個事件,其它微服務可以訂閱這個事件並另一組事件。有時,可以將EventSourcingUnix管道操作進行比較。 基於微服務的系統中的單個事務可以跨越多個微服務,其中我們可以通過一系列事件構建反應式微服務來執行事務。聚合操作的每個狀態更改都可以視為事件,這是系統的不可變事實。

使用CQRS構建聚合視圖的查詢模型


當您使用EventSourcing將一系列事件進行持久化時,您可能需要一種架構方法來解決對微服務的查詢,因為寫模型(Commands)只是一個事件儲存。一種新的架構——命令查詢職責分離(Command Query Responsibility Segregation,CQRS)是實現微服務查詢的理想樣式。顧名思義,CQRS將應用程式劃分為兩個部分:執行更改聚合狀態的操作的命令,以及為聚合視圖提供查詢模型的查詢。雖然CQRS是一種獨立的架構樣式,但我們經常使用EventSourcing作為命令模型,我們也可以為寫操作和查詢操作提供不同的資料庫。這還允許您通過將非規範化資料集加載到讀取模型的資料儲存中來創建高性能的查詢模型。

    

Go語言的EventSourcing和CQRS的示例


使用gRPC, NATS Streaming和CockroachDB等技術構建微服務


示例的代碼可以在此處獲得:https://github.com/shijuvar/go-distributed-sys為了方便理解,我簡化了概念並提供了一個示例,展示如何使用gRPC,NATS等技術構建分佈式系統。

NATS Streaming用於訊息傳遞


EventSourcing架構中,當您從一個微服務中發佈一個事件時,其他微服務可以對這些事件做出反應,併在執行自己的本地事務後發佈另一組事件。基於微服務的系統中的單個事務可以跨越多個微服務,其中我們可以通過一系列的事件來執行事務。每個聚合狀態的更改都可以視為一系列的事件,這是關於系統的不可變事實。為了發佈事件以讓其他微服務知道系統中發生了某些事情,我們需要使用訊息傳遞系統。在此示例中,我們使用NATS Steraming Server作為事件流系統來構建事件驅動的微服務。事件驅動的反應式架構是構建大規模可擴展微服務的架構方法的絕佳選擇。如果您對NATSNATS Streaming不熟悉,請查看我關於基本NATS的文章這裡和NATS Streaming 在這裡[1]。我認為NATS是構建分佈式系統的神經系統,我一直致力於Go生態系統。

gRPC用於構建APIs


在示例中,事件儲存提供了一個API去執行命令,這是一個基於gRPCAPIgRPC是一個高性能、開源的遠程過程呼叫框架,可以在任何地方運行。它使ClientServer能夠透明地進行通信,並使構建連接系統更加容易。gRPC被廣泛稱為微服務中的通信協議。如果您通過API在微服務之間進行通信,那麼gRPC是很好地選擇,如果您不瞭解gRPC,可以查看我的文章[2]

示例演示


下圖是示例代碼的結構:

這是代碼的架構圖:

這是示例中的基本工作流程:

  1. 客戶端應用程式將訂單發佈到HTTP API。

  2. 一個HTTP API(OrderService)接收的命令,然後使用gRPC API創建一個事件,通過一個命令將事件儲存到Event Store,儲存的是事件的不可變的日誌。

  3. Event Store通過API執行命令,然後向NATS Streaming服務器發佈order-created事件,以便讓其他服務知道創建了一個事件。

  4. 付款服務(PaymentService)訂閱事件order-created,然後進行付款,然後通過Event Store API創建另一個事件order-payment-debited

  5. 查詢服務(orderquery-store1和orderquery-store2作為佇列訂閱者)也訂閱了order-created事件,該事件同步資料模型以提供查詢視圖的聚合狀態。

  6. Event Store API在事件儲存上執行命令以創建事件order-payment-debited,並向NATS Streaming服務器發佈事件,以便讓其他服務知道付款已被記入借方。

  7. 餐廳服務(RestaurantService)最終批准訂單。

  8. Distributed Saga管理分佈式事務併在失敗時進行無效事務回滾(暫未實現)

EventSourcing的事件儲存



這是訊息Event的結構體定義。示例中的每個狀態更改都被視為事件,併在Event Store中執行命令。

清單1. 訊息的結構協議緩衝區中的事件:

Event Store提供了一個gRPC API用於儲存事件。這是Event Store實現中的基本代碼塊:

清單2. gRPC服務器中的基本實現:

每當新事件通過其gRPC API作為不可變日誌持久化儲存到Event Store中時,它就會通過NATS Streaming服務器發佈事件,讓其他微服務器知道新事件已經發佈,因此所有用戶微服務都可以對這些事件做出反應。在此示例中,事件從Event Store API本身發佈到訊息傳遞系統(NATS Streaming)中。在實際場景中,它可能來自單個微服務,也可能來自協調單個業務事務跨越多個微服務的Distributed Saga

訂閱用於構建反應式微服務的事件



當您將事件持久儲存到Event Store中時,它通過NATS Streaming發佈新事件,如果您的微服務對這些事件感興趣,則微服務可以對這些事件進行訂閱。在這裡,我們使用NATS Streaming訂閱事件。在此示例中,當創建order-created 事件時,PaymentService訂閱該事件,並創建另一個名為order-payment-debited的事件。

清單3. 對order-created事件做出反應的NATS Streaming訂戶客戶端:

訂閱事件以構建聚合視圖的查詢模型


CQRS中,它將應用程式分為兩部分:命令和查詢。這裡的命令是使用EventSourcing實現的,EventSourcing使用不可變事件日誌的Event Store來構建應用程式狀態。為了創建查詢模型,我們還可以在執行命令操作時訂閱事件。在這個例子中,服務orderquery-store 1orderquery-store2通過QueueGroup訂閱,通過NATS Streaming訂閱事件order-createdNATS中的QueueGroup訂閱服務允許您在不進行任何配置的情況下實現負載均衡。

清單4. 用於同步查詢模型的NATS Streaming QueueGroup訂戶客戶端:

這裡,每當發佈order-created事件時,訂閱者根據事件執行一些邏輯併進行資料同步,通過將資料儲存到資料儲存中來創建聚合的非規範化視圖,它用於CQRS架構中的查詢模型。

使用CockroachDB持久儲存


使用CQRS的一個主要好處是,您可以為寫入操作和查詢操作使用不同的資料模型,因此您還可以使用不同的資料庫技術。在此示例演示中,我們使用CockroachDB執行命令和查詢模型。CockroachDB是一個分佈式資料庫,用於構建可在災難中恢復的全球可擴展雲服務。在Go應用程式中,您可以使用類似於PostgreSQL的驅動程式(如github.com/lib/pq)來處理SQL 。如果您正在使用package database/sql進行事務,請使用CockroachDB的官方庫github.com/cockroachdb/cockroach-go/crdb。在示例演示中,使用CockroachDB的持久性邏輯在代碼的store包中實現。g官方包crdb的方法ExecuteTx可以幫助您將事務執行到CockroachDB中。

清單5. 使用包crdbCockroachDB中的事務實現:

這是Event Store的不可變日誌,用於創建最終的訂單:

Event Store的事件表有一個名為eventdata的欄位,我們將整個事件資料作為JSON文件儲存到該欄位,它對構建應用程式狀態以及構建查詢視圖很有用。


    

原始碼


示例演示的原始碼可在此處獲得:https://github.com/shijuvar/go-distributed-sys,可以點擊文末閱讀原文進行查看。

    

總結


本文的主要目的是通過EventSourcingCQRS來構建事件驅動的微服務。構建真實世界的微服務是非常複雜的,其中最困難的部分是處理分散在各個微服務擁有的幾個資料庫中的資料。這使得構建跨多個微服務的業務事務變得非常複雜,並且查詢資料時對多個資料庫無法進行關聯查詢。因此,您必須使用一些架構方法從現實世界的角度構建微服務,您可以考慮使用事件驅動的體系結構,如EventSourcingEventSourcing架構最好與CQRS結合使用。



原文鏈接



[1]: https://medium.com/@shijuvar/building-distributed-systems-and-microservices-in-go-with-nats-streaming-d8b4baa633a2

[2]: https://medium.com/@shijuvar/building-high-performance-apis-in-go-using-grpc-and-protocol-buffers-2eda5b80771b

英文原文:https://medium.com/@shijuvar/building-microservices-with-event-sourcing-cqrs-in-go-using-grpc-nats-streaming-and-cockroachdb-983f650452aa

代碼地址:https://github.com/shijuvar/go-distributed-sys



赞(0)

分享創造快樂