Event Sourcing - ENode(三)

接上一篇html

http://www.cnblogs.com/dopeter/p/4903328.html設計模式

老闆昨天在第二篇介紹中回覆代碼和文字沒法一一對應。爲了更好的讓老闆爲你們解惑,把第二篇最後的猜想的問題搞清楚後,就補上其餘文字說明的代碼圖。架構

在上篇中泛泛介紹了Commanding,比較跳躍,目前是想到哪寫到哪,後續分門別類的整理。併發

 

在後續中會補全ENode框架的裝配關係,其實做者的接口命名已經很是清楚了。app

不管做者使用了什麼樣的裝配的設計模式,目的都是爲了更好的擴展與維護。通常能直接組合的就直接組合,能隔離的就直接隔離,若是遇到沒法處理的狀況有句經典的話,當咱們遇到沒法解決的問題的時候,就增長一箇中間層來專門解決這個問題。其實和現實中是很匹配的,仍是借鑑了現實的智慧啊。同事A,同事B、我,當A與B在一件事有爭執的時候,那我就充當Broker的角色吧,若是這件事很是重要,頗有可能出現矛盾,因而我可能會求助他人,例如同事C或者領導,讓他們分別與A、B溝通,又也許我會集中精神,在A的面前與B的面前使用不一樣的方式來溝通,那麼就是Proxy了。呵呵,想到了就寫下來了。繼續正題。框架

 

EDA異步


目前很火的架構模型,這裏的Event不是DDD中的Domain Event,純粹是指這種架構風格的Event,固然某些狀況下咱們能夠這麼認爲,不過這裏先不套用。分佈式

 

假如如今在一個分佈式的系統中,有2個子系統實例,ServiceA與ServiceB。微服務

假如ServiceA的某一個功能是建立Account,ServiceB的某一個功能是發送郵件。高併發

如今有個系統級的功能是當一個Account被建立後,向這個Account發送郵件。

那麼ServiceA建立好Account後,會通知ServiceB發送郵件。它們之間會進行通信。

 

Event能夠認爲是一種它們之間傳遞消息的模型,例如ServiceA建立了一個Account併發佈一個Event叫作OnAccountCreated,固然在咱們實際的實現中,會被描述成各類不一樣的類,在ENode中這個Event消息被做者定義爲Message,Message就是一個Event,在一個Event裏面會包含這個Event的信息,例如OnAccountCreated事件裏面包含了新增帳戶的郵箱。

還有另一種模型,就是咱們傳統實現的模型,能夠借鑑CQRS的Command/Query,例如ServiceA建立了一個Account,調用ServiceB的發送郵件的方法,調用發送郵件就是一個Command,ServiceA命令ServiceB發送郵件。

是否是以爲有點像Pub/Sub與Request/Reply,對應的實現是RPC和MOM。

能夠這麼理解,但也不徹底是。

通常咱們使用Request/Reply的RPC框架,兩邊定義契約,假如咱們這裏的契約是面向功能,例如ServiceB定義一個SendMail(Account account);當ServiceA完成建立Account後就能夠調用這個接口發送郵件。

若是咱們使用Request/Reply能不能實現Event呢,徹底能夠,ServiceA使用RPC框架發送一個OnAccountCreated的事件至ServiceB,在OnAccountCreated事件中包含了Account的信息,ServiceB開始發送郵件。Event的處理有不少優雅實現,最簡單暴力的方式是直接規定一個通用接口,根據傳遞過來的事件處理邏輯。

問題就在這裏,使用RPC咱們須要知道對方的契約,即使是REST咱們也要知道URL即資源地址。經過Event的Wrapper咱們其實是消除了RPC當中雙方的契約,REST這邊的資源地址。咱們的契約面向的是什麼角度,也就是解耦了什麼角度,例如若是咱們契約是業務型契約,就是解耦了業務也能夠認爲是功能,ServiceA不用知道ServiceB有發送郵件的功能。ServiceA只須要發送一個Event至ServiceB便可,也許不是OnAccountCreated事件。

這樣經過Event咱們實現了消息層面的解耦,本質上Event是一個消息的抽象。Event包裹了真實的業務消息,再次證實中間層這句話的適用性。不過這句話還有後面半截,這裏不扯遠了。

可是這時ServiceA是必須知道ServiceB的,MOM能夠不讓ServiceA沒必要知道ServiceB。

因此MOM+Event實現了分佈式的EDA,對ServiceA來講,不用知道在建立完Account後下一步須要作什麼,由誰來作。經過MOM來隔離主從關係。

 

從另一個角度來看Request/Reply以及Pub/Sub。

Request/Reply發送Command,仍是剛纔的場景,ServiceB須要返回結果或者不返回結果,從組件運行層面來看,它是同步的。咱們能夠這樣實現,使用Event,例如ServiceA發送OnAccountCreated事件至ServiceB,ServiceB發送OnMailSendCompleted事件至ServiceA,若是這個業務場景ServiceA必須獲得郵件發送成功才能執行後續操做,那麼仍然是同步的,另外種解釋咱們能夠認爲是同步非阻塞的。

Pub/Sub+Event,咱們也就能夠認爲是異步非阻塞的。

 

那麼EDA的真正威力就體現出來,可擴展性,吞吐量,都會上升。

 

EDA In ENode


 

說了不少EDA的話題,仍是來ENode裏面看看做者的實現。

前面介紹過,ENode分佈式的粒度,在開發者應用層範圍內定義了4種Event :Application Message、Command、Domain Event、Exception,能夠將他們認爲是EDA中Event在CQRS框架場景中的實例,其實這麼說並不許確,應該是在ENode框架中做者定義好要用Event包裹的框架消息類型。EDA的組合還會有個MOM,則是做者本身實現並開源的EQueue。下圖中所示的不是EQueue項目,其實是EQueue在ENode項目中的Proxy。

 

一目瞭然,分別是4種消息的Pub/Sub。讓咱們看看其中一個的實現,就更清楚了。

先看最簡單的ApplicationMessage

 

Consumer 消費者,Subscribe(string topic);訂閱一個主題,這裏就可以看到MOM的存在。

IQueueMessageHandler.Handle(QueueMessage queueMessage, IMessageContext context)方法,Consume還充當了一個Dispatcher的角色。

 

 

Publisher 發佈者,PublishAsync方法,發佈一個消息,這裏爲何沒有Topic呢,以下圖主題。

 

做者已經拆分出去了,在每個子系統裏面能夠定義開始所說4種消息類型的不一樣。咱們能夠自由的組合,例如將DDD中的應用層定義爲一個Project,Domain定義爲一個Project,橫向或者縱向的擴展都是可行的,其實能夠對應目前比較火的一種架構模型,微服務。

 

讓咱們繼續看看Command

 

Consumer 消費者

CommandMessage EQueue傳遞的信息,以下圖所示

注意ReplyAddress,這是後面介紹要用到的屬性。

 

CommandExecutedMessage 已經被處理過的Command消息

CommandResultProcessor 命令結果處理者 在這裏不只包括Command被處理的結果,還能夠處理當前這條Command觸發的Domain Event處理的結果,以下圖所示。

能夠經過CommandReplyType判別是Command仍是DomainEvent的回執。

 

執行一個Command並得到這條Command處理的結果,是在執行一個Command時指定的。以下圖

這裏實際上就是前面介紹EDA所描述的Request/Reply+Event的方式,能夠是同步非阻塞也能夠是異步非阻塞,以下圖所示

通常都遵照CQRS的原則,Command無返回而且是異步,做者這裏經過Fututre的方式來獲取Command執行的回執,針對的是必定要獲得結果以後再繼續下面邏輯的場景。有時可能也想直接獲得結果,例如上圖所示。這也是做者考慮到不少場景但不生搬硬套CQRS。

 

做者實現回執的方式以下面2張圖所示

 

Consumer接收到消息後,處理完畢後,若是發現CommandMessage中的ReplyAddress存在,則經過SendReplyService向這個地址發送回執消息。

 

剩下的Domain Event和Exception也是差很少的Pub/Sub的組成。就不一一介紹了。

 

做者關於博文的說明


 

做者的說明在回覆中,方便感興趣的朋友查看,就複製上來了。

 

By netfocus:

Application Message、Command、Domain Event、Exception這些是ENode中定義的4種消息,他們都實現了IMessage接口。IMessage是ENode中定義的一個通用的 消息接口。但和EQueue中的消息是兩個層次的。EQueue中的消息的內容(payload)纔是ENode中的IMessage。架 構層面,通常流行的就兩種架構:SOA,EDA。SOA屬於RPC風格,通常是阻塞的,高併發時,吞吐量因爲服務之間有依賴關係,因此整個系統的吞吐量受 限於最慢的服務的吞吐量;若是是EDA,屬於PUB-SUB的風格,服務之間徹底解耦,經過消息的topic來發生邏輯上的關係。系統的吞吐量不受任何一 個節點的限制;具體使用哪一種場景要看狀況而定,通常系統之間交互,仍是用SOA風格的,系統內部的組件之間通訊,我以爲EDA更好。ENode.EQueue 是一個防腐層。用於把ENode和EQueue鏈接起來,但確保ENode,EQueue相互不知道對方的存在。ENode是一個EDA架構的風格框架, 因此須要一個分佈式消息中間件,EQueue就是我開發的分佈式消息中間件。若是咱們要用其餘的消息中間件,能夠本身和其餘消息中間件整合便可,好比寫一 個ENode.RabbitMQ,ENode.Kafka,ENode.RocketMQICommandService中提供的幾個方 法,比傳統的CQRS架構的定義確實要豐富一點,目的也是爲了增長框架的實用性。具體使用哪一個方法由開發者本身決定。一般通常咱們在後臺管理系統,但願等 讀庫也更新後才返回Command的,則能夠調用ExecuteAsync並設置爲等event也處理完後再返回的方式。若是是前臺,用戶不須要當即知道 處理結果的場景,則建議用SendAsync或Send便可。做爲一個CQRS框架,我鼓勵你們儘可能考慮使用無返回值的方式,不然CQRS的效果就會打折 扣。ENode做爲一個框架,可使用不少的OO特性,我以爲全部的OO設計原則或設計模式的核心就是隔離變化點,因此當我設計框架時, 首先要定義接口,明確接口的職責,而後接口實現類裏發現有些東西是變化的,則進一步提煉其餘接口出來,最後能夠確保任何實現類內部使用的都是接口,這樣整 個框架能夠說任何地方均可以替代,增長了框架的可擴展性。

相關文章
相關標籤/搜索