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

使用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)

分享創造快樂