你們晚上好,我是鄭承良,跟你們分享的話題是《基於Actor模型的CQRS/ES解決方案分享》,最近一段時間我一直是這個話題的學習者、追隨者,這個話題目前生產環境落地的資料少一些,分享的內容中有一些我我的的思考和理解,若是分享的內容有誤、有疑問歡迎你們提出,但願經過分享這種溝通方式你們相互促進,共同進步。git
多核處理器出現後,你們經常使用的併發編程模型是共享內存模型。程序員
這種編程模型的使用帶來了許多痛點,好比:github
簡單總結:算法
Actor模型是一個概念模型,用於處理併發計算。Actor由3部分組成:狀態(State)+行爲(Behavior)+郵箱(Mailbox),State是指actor對象的變量信息,存在於actor之中,actor之間不共享內存數據,actor只會在接收到消息後,調用本身的方法改變本身的state,從而避免併發條件下的死鎖等問題;Behavior是指actor的計算行爲邏輯;郵箱創建actor之間的聯繫,一個actor發送消息後,接收消息的actor將消息放入郵箱中等待處理,郵箱內部經過隊列實現,消息傳遞經過異步方式進行。數據庫
Actor是分佈式存在的內存狀態及單線程計算單元,一個Id對應的Actor只會在集羣種存在一個(有狀態的 Actor在集羣中一個Id只會存在一個實例,無狀態的可配置爲根據流量存在多個),使用者只須要經過Id就能隨時訪問不須要關注該Actor在集羣的什麼位置。單線程計算單元保證了消息的順序到達,不存在Actor內部狀態競用問題。編程
舉個例子:設計模式
多個玩家合做在打Boss,每一個玩家都是一個單獨的線程,可是Boss的血量須要在多個玩家之間同步。同時這個Boss在多個服務器中都存在,所以每一個服務器都有多個玩家會同時打這個服務器裏面的Boss。api
若是多線程併發請求,默認狀況下它只會併發處理。這種狀況下可能形成數據衝突。可是Actor是單線程模型,意味着即便多線程來經過Actor ID調用同一個Actor,任何函數調用都是隻容許一個線程進行操做。而且同時只能有一個線程在使用一個Actor實例。服務器
Actor模型這麼好,怎麼實現?網絡
能夠經過特定的Actor工具或直接使用編程語言實現Actor模型,Erlang語言含有Actor元素,Scala能夠經過Akka框架實現Actor編程。C#語言中有兩類比較流行,Akka.NET框架和Orleans框架。此次分享內容使用了Orleans框架。
特色:
Erlang和Akka的Actor平臺仍然使開發人員負擔許多分佈式系統的複雜性:關鍵的挑戰是開發管理Actor生命週期的代碼,處理分佈式競爭、處理故障和恢復Actor以及分佈式資源管理等等都很複雜。Orleans簡化了許多複雜性。
優勢:
缺點:
第一小節總結:上面內容由下往上,從代碼層面細粒度層面表達了採用Actor模型的好處或緣由。
在秒殺場景中,因爲對樂觀鎖/悲觀鎖的使用,推測系統響應時間更復雜。
1000萬用戶,一個用戶一個Actor,1000萬個內存對象。
200萬件SKU,一件SKU一個Actor,200萬個內存對象。
總結:
因爲1000萬+用戶的請求根據購物意願分散到200萬個商品SKU上: 每一個內存領域對象都強制串行執行用戶請求,避免了競爭爭搶; 內存領域對象上扣庫存操做處理時間極快,基本沒可能出現請求阻塞狀況;
從架構層面完全解決高併發爭搶的性能問題。 理論模型,TPS>100萬+……
Actor是分佈式存在的內存狀態及單線程計算單元,採用EventSourcing只記錄狀態變化引起的事件,事件落盤時只有Add操做,上述設計中很依賴Actor中State,事件溯源提升性能的同時,能夠用來保證內存數據的高可用。
上面1000萬併發場景的內容來自網友分享的PPT,與咱們實際項目思路一致,就拿來與你們分享這個過程,下圖是咱們交易所項目中的架構圖:
開源版本架構圖:
( 開源項目github:https://github.com/RayTale/Ray )
第二小節總結:由上往下,架構層面粗粒度層面表達了採用Actor模型的好處或緣由。
系統開發完成後Actor要組成集羣,系統在集羣中部署,實現高性能、高可用、可伸縮的要求。部署階段能夠選擇Service Fabric或者K8S,目的是下降分佈式系統部署、管理的難度,同時知足彈性伸縮。
交易所項目能夠採用Service Fabric部署,也能夠採用K8S,當時K8S還沒這麼流行,咱們採用了Service Fabric,Service Fabric 是一款微軟開源的分佈式系統平臺,可方便用戶輕鬆打包、部署和管理可縮放的可靠微服務和容器。開發人員和管理員不需解決複雜的基礎結構問題,只需專一於實現苛刻的任務關鍵型工做負荷,即那些可縮放、可靠且易於管理的工做負荷。支持Windows與Linux部署,Windows上的部署文檔齊全,但在Linux上官方資料沒有。如今推薦K8S。
第三小節總結:
上面是我對今天話題的分享。
參考:
T: 1000W用戶,購買200W SKU,若是不考慮熱點SKU,則每一個SKU平均爲5個併發減庫存的更新; 而總共的SKU分10個數據庫存儲,則每一個庫存儲20W SKU。因此20W * 5 = 100W個併發的減庫存;
T: 每一個庫負責100W的併發更新,這個併發量,不論是否採用actor/es,都要採用group commit的技術
T: 不然單機都不可能達到100W/S的數據寫入。
T: 採用es的方式,就是每秒插入100W個事件;不採用ES,就是每秒更新100W次商品減庫存的SQL update語句
Y: 哦
T: 不過實際上,除了阿里的體量,不可能併發達到1000W的
T: 1000W用戶不表明1000W併發
T: 若是真的是1000W併發,可能實際在線用戶至少有10億了
T: 由於若是隻有1000W在線用戶,那是不可能這些用戶同時在同一秒內發起購買的,你們想一下是否是這樣
Y: 這麼熟的名字
T: 因此,1000W在線用戶的併發實際只有10W最多了
T: 也就是單機只有1W的併發更新,不須要group commit也無壓力
Y: 嗯
Q1:單點故障後,正在處理的 cache 數據如何處理的,例如,http,tcp請求…畢竟涉及到錢
A:actor有激活和失活的生命週期,激活的時候使用快照和Events來恢復最新內存狀態,失活的時候保存快照。actor框架保證系統中同一個key只會存在同一個actor,當單點故障後,actor會在其它節點重建並恢復最新狀態。
Q2:event ID生成的速度如何保證有效的scale?有沒有遇到須要後期插入一些event,修正前期系統運行的bug?有沒有遇到須要把前期已經定好的event再拆細的狀況?有遇到系統錯誤,須要replay event的狀況? A:1. 當時項目中event ID採用了MongoDB的ObjectId生成算法,沒有遇到問題;有遇到後期插入event修正以前bug的狀況;有遇到將已定好的event修改的狀況,採用的方式是加版本號;沒有,遇到過系統從新遷移刪除快照從新replay event的狀況。
Q3:數據落地得策略是什麼?仍是說就是直接落地? A:event數據直接落地;用於支持查詢的數據,是Handler消費event後異步落庫。
Q4:actor跨物理機器集羣事務怎麼處理? A:結合事件溯源,採用最終一致性。
Q5:Grain Persistence使用Relational Storage容量和速度會不會是瓶頸? A:Grain Persistence存的是Grain的快照和event,event是隻增的,速度沒有出現瓶頸,並且開源版本測試中PostgreSQL性能優於MongoDB,在存儲中針對這兩個方面作了優化:好比分表、歸檔處理、快照處理、批量處理。
Q6:SF中的reliable collection對應到k8s是什麼? A:很差意思,這個我不清楚。
Q7:開發語言是erlang嗎?Golang有這樣的開發模型庫支持嗎? A:開發語言是C#。Golang我瞭解的很少,proto.actor能夠了解一下:https://github.com/AsynkronIT/protoactor-go
Q8:可否來幾篇博客闡述如何一步步使用orleans實現一個簡單的事件總線 A:事件總線的實現使用的是RabbitMQ,這個能夠看一下開源版本的源碼EventBus.RabbitMQ部分,博客的可能後面會寫,若是不996的話(笑臉)
Q9:每一個pod的actor都不同,如何用k8s部署actor,失敗的節點如何監控,並藉助k8s自動恢復? A:actor是無狀態的,失敗恢復依靠從新激活時事件溯源機制。k8s部署actor官方有支持,能夠參考官方示例。在實際項目中使用k8s部署Orleans,我沒有實踐過,後來有同事驗證過能夠,具體如何監控不清楚。
Q10:Orleans中,持久化事件時,是否有支持併發衝突的檢測,是如何實現的? A:Orleans不支持;工做中,在事件持久化時作了這方面的工做,方式是根據版本號。
Q11:Orleans中,如何判斷消息是否重複處理的?由於分佈式環境下,同一個消息可能會被重複發送到actor mailbox中的,而actor自己沒法檢測消息是否重複過來。 A:是的,在具體項目中,經過框架封裝實現了冪等性控制,具體細節是經過插入事件的惟一索引。
Q12:同一個actor是否會存在於集羣中的多臺機器?若是可能,怎樣的場景下可能會出現這種狀況? A:一個Id對應的Actor只會在集羣種存在一個。
Q13: 響應式架構 消息模式Actor實現與Scala.Akka應用集成 這本書對理解actor的幫助大嗎,還有實現領域驅動設計這本
A:這本書我看過,剛接觸這個項目時看的,文章說的有些深奧,由於當時關注的是Orleans,文中講的是akka,幫助不大,推薦具體項目的官方文檔。實現領域驅動這本書有收穫,推薦專題式閱讀,DDD多在社區交流。