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

2021-12-25 容器時代

導讀:

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

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

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

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

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

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

使用EventSourcingCQRS構建微服務

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

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

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

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

使用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作為事件流系統來構建事件驅動的微服務。事件驅動的反應式架構是構建大規模可擴展微服務的架構方法的絕佳選擇。如果您對NATS和NATS Streaming不熟悉,請查看我關於基本NATS的文章這裡和NATS Streaming 在這裡[1]。我認為NATS是構建分布式系統的神經系統,我一直致力於Go生態系統。

gRPC用於構建APIs

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

示例演示

下圖是示例代碼的結構:

這是代碼的架構圖:

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

客戶端應用程式將訂單發布到HTTP API。

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

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

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

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

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

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

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 1和orderquery-store2通過QueueGroup訂閱,通過NATS Streaming訂閱事件order-created。NATS中的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. 使用包crdb在CockroachDB中的事務實現:

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

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


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

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

相關焦點

  • 使用Golang語言建造EventSourcing/CQRS的微服務
    使用EventSourcing和CQRS構建事件驅動的微服務為了解決微服務的實際挑戰,事件驅動的、反應式的系統在領域驅動設計中結合使用是一個很好的辦法。因此,我強烈建議使用EventSourcing,這是以事件為中心的架構,通過編寫各種事件來構建應用程式狀態。
  • CockroachDB應用接入指南
    CockroachDB 內置了生成 HAProxy 配置文件的功能,安裝 HAProxy 後可以直接使用。在節點192.168.180.21上生成 HAProxy 配置文件 haproxy.cfg ,生成方式:$ cd /home/cockroach/CockroachDB$ .
  • CockroachDB 2.0版本全新升級
    表分區功能:允許用戶對數據做行級別的分區和存儲地域的控制,減少訪問延時集群拓撲圖:在 Admin UI上新增的Node Map功能,能實時呈現集群拓撲信息基於角色的訪問控制:簡化訪問控制方式,允許對用戶組統一授權實時備份恢復:使用備份數據恢復到指定時間點狀態支持JSON格式的數據存儲Sequences功能:按照自定義規則定義一組序列化的整數
  • CockroachDB重磅來襲!
    他們三人在Google期間使用過Colossus,BigTable,Spanner,當離開Google後,因為這些系統並未開源,他們無法在自己新的項目中使用到這些系統,所以他們決定以開源的方式克隆類似的系統。
  • 微服務架構及其最重要的 10 個設計模式
    這裡我使用資料庫這一術語來表示邏輯上的數據隔離,也就是說微服務可以共享物理資料庫,但應該使用分開的數據結構、集合或者表,這還將有助於確保微服務是按照領域驅動設計的方法正確拆分的。akkatecture, Axon,Eventuate延伸閱讀事件驅動https://martinfowler.com/eaaDev/EventSourcing.html事件驅動模式-雲設計模式https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing
  • 了解gRPC框架
    --grpc_python_out=.快速使用 gRPC 框架This guide gets you started with gRPC in Python with a simple working example.
  • gRPC庫C++構建及示例
    C++開發者做什麼事情都不太容易,網絡編程原本已經很艱難了,想要使用gRPC來降低難度,庫構建又是一道坎兒.這裡展示如何在殘酷的網絡環境下使用CMake構建gRPC庫,並附帶示例驗證庫構建結果.如何下載gRPC庫原始碼?
  • 領域驅動設計的實踐 – CQRS & Event Sourcing
    以上便是DDD的一些基本概念,作為開發者而言,我並不贊同概念的堆砌和教條主義,其實很多時候,我們已經不自覺的使用了DDD的一些概念潛移默化的指導我們平時的軟體開發,比如我們會在開發的小組內,使用約定俗稱的名詞,開發者和業務員都能明白這些沒有歧義的名詞(通用語言), 開發者也會站在業務員的角度思考軟體系統內部設計分割的原則(聚合/聚合根的設計)。
  • CockroachDB 可視化管理工具TablePlus使用介紹
    由於CockroachDB 支持 PG 協議,用戶可以使用第三方工具(如DBeaver、TablePlus)進行管理。後續,CockroachDB 2.1正式版本計劃將會支持 PGAdmin4。第三方工具 TablePlus 在今年推出的 TablePlus Build 100版本開始支持 CockroachDB。
  • gRPC介紹
    開篇語關於grpc用法我只是基本的使用,如果我用的方式不對,煩請指出來,一起討論。介紹imggRPC是一個由google開發的,跨語言的,高性能遠程調用框架,使客戶端和服務端應用程式可以透明的進行通訊,並簡化了連接系統的構建,使用http/2作為通信協議,使用protocol buffers作為序列化協議。客戶端應用程式可以直接在其他計算機上的伺服器應用程式上調用該方法,就好像它是本地對象一樣。
  • 使用Spring Boot和Docker構建微服務架構
    本文是《使用Spring Boot和Docker構建微服務架構》系列四部曲的前兩篇,對微服務架構以及容器化概念作一個概述,
  • gRPC使用簡介
    用protoc就能使用proto文件幫助我們生成上面的option層代碼。在gRPC中,客戶端應用程式可以直接在另一臺計算機上的伺服器應用程式上調用方法,就好像它是本地對象一樣,從而使您更輕鬆地創建分布式應用程式和服務。gRPC的調用模型如下
  • 使用NATS的Synadia自適應邊緣架構介紹
    注意,我們正在走向零信任,因此在操作員模式中,即使使用帳戶和用戶的概念,NATS系統也不會存儲或訪問私有NATS密鑰。https://docs.nats.io/nats-tools/nsc/nsc#creating-an-operator-account-and-user當NATS客戶端連接時,它的憑證表明它屬於一個特定的帳戶。
  • 如何使用 Node.js 和 Docker 構建高質量的微服務
    微服務的服務範圍越來越廣泛,尤其是在構建複雜應用中,下面我主要從以下幾點分享如何使用 Node.js 和 Docker 構建高質量的微服務
  • 使用微服務構建雲Web服務
    =======使用微服務構建雲Web服務提供了一些優點,諸如可擴展性等,並允許企業應用程式訪問新功能和工具。  亞馬遜公司和微軟公司作為行業領先的公共雲提供商,都提供了幾十個Web服務,每個Web服務代表一個有用的應用程式功能或工具,用戶可以通過API進行調用。在表面上,這些產品看起來很像微服務。
  • 通過構建微服務來學習Docker
    在本文中,我將展示Docker是如何工作的,以及應用Docker完成構建一個基本的微服務開發任務。我們將使用一個簡單的Node.js服務與一個MySQL後端為例,實現從本地運行的代碼遷移到容器化運行的微服務和資料庫。它的核心就是:Docker是一個允許你創建鏡像(這包含了很多步驟,就像在虛擬機的模板一樣)並且讓這個鏡像的實例運行在容器中的軟體。
  • 使用Spring Cloud和Docker構建微服務
    我已經在GitHub(https://github.com/kbastani/spring-cloud-microservice-example)上創建了一個實例項目,這個項目是一個端到端的原生雲平臺,使用Spring Cloud構建實際的微服務架構。
  • 【DB寶79】使用OGG微服務快速雙向同步RDS資料庫
    2、使用華為雲或天翼雲自帶的數據同步功能。這個也不可行,翻閱了一下文檔,同步只能全量+增量同步,這對於雙向同步來說不可行。3、使用ogg遠程捕獲投遞。ogg for MySQL從MySQL 5.7和ogg 19c開始支持遠程捕獲(Remote Capture)和遠程投遞(Remote Delivery),所以配置雙向同步,該方案經過驗證也是可行的!
  • gRPC Golang/Python 使用
    工作中使用到 gRPC, 其實 http 請求也是一種 rpc 變種,遠程進程調用.gRPC底層是 HTTP2 協議。gRPC 一開始由 google 開發,是一款語言中立、平臺中立、開源的遠程過程調用 (RPC) 系統,面向移動和 HTTP/2 設計。
  • 使用 ServerLess, Nodejs, MongoDB Atlas 構建 REST API
    本篇文章中我們將使用 ServerLess、MongoDB Atlas cloud 與 Node.js 的結合來快速構建一個 REST API,無論你是前端工程師還是後端工程師,只要你掌握一些 JavaScript 基礎語法就可以完成。通過本文你能學到什麼?