精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

談一下關于CQRS架構如何實現高性能

移動開發
前不久,看到朋友寫了一篇文章,其中的觀點是,要想高性能,需要盡量:避開網絡開銷(IO),避開海量數據,避開資源爭奪。對于這3點,我覺得很有道理。所以也想談一下,CQRS架構下是如何實現高性能的。

CQRS架構簡介

關于CQRS(Command Query Responsibility Segration)架構,大家應該不會陌生了。簡單的說,就是一個系統,從架構上把它拆分為兩部分:命令處理(寫請求)+查詢處理(讀請求)。然后讀寫兩邊可以用不同的架構實現,以實現CQ兩端(即Command Side,簡稱C端;Query Side,簡稱Q端)的分別優化。CQRS作為一個讀寫分離思想的架構,在數據存儲方面,沒有做過多的約束。所以,我覺得CQRS可以有不同層次的實現,比如:

  1. CQ兩端數據庫共享,CQ兩端只是在上層代碼上分離;這種做法,帶來的好處是可以讓我們的代碼讀寫分離,更好維護,且沒有CQ兩端的數據一致性問題,因為是共享一個數據庫的。我個人認為,這種架構很實用,既兼顧了數據的強一致性,又能讓代碼好維護。
  2. CQ兩端數據庫和上層代碼都分離,然后Q的數據由C端同步過來,一般是通過Domain Event進行同步。同步方式有兩種,同步或異步,如果需要CQ兩端的強一致性,則需要用同步;如果能接受CQ兩端數據的最終一致性,則可以使用異步。采用這種方式的架構,個人覺得,C端應該采用Event Sourcing(簡稱ES)模式才有意義,否則就是自己給自己找麻煩。因為這樣做你會發現會出現冗余數據,同樣的數據,在C端的db中有,而在Q端的db中也有。和上面***種做法相比,我想不到什么好處。而采用ES,則所有C端的***數據全部用Domain Event表達即可;而要查詢顯示用的數據,則從Q端的ReadDB(關系型數據庫)查詢即可。

我覺得要實現高性能,可以談的東西還有很多。下面我想重點說說我想到的一些設計思路:

避開資源爭奪

秒殺活動的例子分析

我覺得這是很重要的一點。什么是資源爭奪?我想就是多個線程同時修改同一個數據。就像阿里秒殺活動一樣,秒殺開搶時,很多人同時搶一個商品,導致商品的庫存會被并發更新減庫存,這就是一個資源爭奪的例子。一般如果資源競爭不激烈,那無所謂,不會影響性能;但是如果像秒殺這種場景,那db就會抗不住了。在秒殺這種場景下,大量線程需要同時更新同一條記錄,進而導致MySQL內部大量線程堆積,對服務性能、穩定性造成很大傷害。那怎么辦呢?我記得阿里的丁奇寫過一個分享,思路就是當MySQL的服務端多個線程同時修改一條記錄時,可以對這些修改請求進行排隊,然后對于InnoDB引擎層,就是串行的。這樣排隊后,不管上層應用發過來多少并行的修改同一行的請求,對于MySQL Server端來說,內部總是會聰明的對同一行的修改請求都排隊處理;這樣就能確保不會有并發產生,從而不會導致線程浪費堆積,導致數據庫性能下降。這個方案可以見下圖所示:

 

方案圖

如上圖所示,當很多請求都要修改A記錄時,MySQL Server內部會對這些請求進行排隊,然后一個個將對A的修改請求提交到InnoDB引擎層。這樣看似在排隊,實際上會確保MySQL Server不會死掉,可以保證對外提供穩定的TPS。

但是,對于商品秒殺這個場景,還有優化的空間,就是Group Commit技術。Group Commit就是對多個請求合并為一次操作進行處理。秒殺時,大家都在購買這個商品,A買2件,B買3件,C買1件;其實我們可以把A,B,C的這三個請求合并為一次減庫存操作,就是一次性減6件。這樣,對于A,B,C的這三個請求,在InnoDB層我們只需要做一次減庫存操作即可。假設我們Group Commit的每一批的size是50,那就是可以將50個減操作合并為一次減操作,然后提交到InnoDB。這樣,將大大提高秒殺場景下,商品減庫存的TPS。但是這個Group Commit的每批大小不是越大越好,而是要根據并發量以及服務器的實際情況做測試來得到一個***的值。通過Group Commit技術,根據丁奇的PPT,商品減庫存的TPS性能從原來的1.5W提高到了8.5W。

從上面這個例子,我們可以看到阿里是如何在實際場景中,通過優化MySQL Server來實現高并發的商品減庫存的。但是,這個技術一般人還真的不會!因為沒多少人有能力去優化MySQL的服務端,排隊也不行,更別說Group Commit了。這個功能并不是MySQL Server自帶的,而是需要自己實現的。但是,這個思路我想我們都可以借鑒。

CQRS如何實現避免資源競爭

那么對于CQRS架構,如何按照這個思路來設計呢?我想重點說一下我上面提到的第二種CQRS架構。對于C端,我們的目標是盡可能的在1s內處理更多的Command,也就是數據寫請求。在經典DDD的四層架構中,我們會有一個模式叫工作單元模式,即Unit of Work(簡稱UoW)模式。通過該模式,我們能在應用層,一次性以事務的方式將當前請求所涉及的多個對象的修改提交到DB。微軟的EF實體框架的DbContext就是一個UoW模式的實現。這種做法的好處是,一個請求對多個聚合根的修改,能做到強一致性,因為是事務的。但是這種做法,實際上,沒有很好的遵守避開資源競爭的原則。試想,事務A要修改a1,a2,a3三個聚合根;事務B要修改a2,a3,a4;事務C要修改a3,a4,a5三個聚合根。那這樣,我們很容易理解,這三個事務只能串行執行,因為它們要修改相同的資源。比如事務A和事務B都要修改a2,a3這兩個聚合根,那同一時刻,只能由一個事務能被執行。同理,事務B和事務C也是一樣。如果A,B,C這種事務執行的并發很高,那數據庫就會出現嚴重的并發沖突,甚至死鎖。那要如何避免這種資源競爭呢?我覺得我們可以采取三個措施:

讓一個Command總是只修改一個聚合根

這個做法其實就是縮小事務的范圍,確保一個事務一次只涉及一條記錄的修改。也就是做到,只有單個聚合根的修改才是事務的,讓聚合根成為數據強一致性的最小單位。這樣我們就能***化的實現并行修改。但是你會問,但是我一個請求就是會涉及多個聚合根的修改的,這種情況怎么辦呢?在CQRS架構中,有一個東西叫Saga。Saga是一種基于事件驅動的思想來實現業務流程的技術,通過Saga,我們可以用最終一致性的方式最終實現對多個聚合根的修改。對于一次涉及多個聚合根修改的業務場景,一般總是可以設計為一個業務流程,也就是可以定義出要先做什么后做什么。比如以銀行轉賬的場景為例子,如果是按照傳統事務的做法,那可能是先開啟一個事務,然后讓A賬號扣減余額,再讓B賬號加上余額,***提交事務;如果A賬號余額不足,則直接拋出異常,同理B賬號如果加上余額也遇到異常,那也拋出異常即可,事務會保證原子性以及自動回滾。也就是說,數據一致性已經由DB幫我們做掉了。

但是,如果是Saga的設計,那就不是這樣了。我們會把整個轉賬過程定義為一個業務流程。然后,流程中會包括多個參與該流程的聚合根以及一個用于協調聚合根交互的流程管理器(ProcessManager,無狀態),流程管理器負責響應流程中的每個聚合根產生的領域事件,然后根據事件發送相應的Command,從而繼續驅動其他的聚合根進行操作。

轉賬的例子,涉及到的聚合根有:兩個銀行賬號聚合根,一個交易(Transaction)聚合根,它用于負責存儲流程的當前狀態,它還會維護流程狀態變更時的規則約束;然后當然還有一個流程管理器。轉賬開始時,我們會先創建一個Transaction聚合根,然后它產生一個TransactionStarted的事件,然后流程管理器響應事件,然后發送一個Command讓A賬號聚合根做減余額的操作;A賬號操作完成后,產生領域事件;然后流程管理器響應事件,然后發送一個Command通知Transaction聚合根確認A賬號的操作;確認完成后也會產生事件,然后流程管理器再響應,然后發送一個Command通知B賬號做加上余額的操作;后續的步驟就不詳細講了。大概意思我想已經表達了。總之,通過這樣的設計,我們可以通過事件驅動的方式,來完成整個業務流程。如果流程中的任何一步出現了異常,那我們可以在流程中定義補償機制實現回退操作。或者不回退也沒關系,因為Transaction聚合根記錄了流程的當前狀態,這樣我們可以很方便的后續排查有狀態沒有正常結束的轉賬交易。具體的設計和代碼,有興趣的可以去看一下ENode源代碼中的銀行轉賬的例子,里面有完整的實現。

對修改同一個聚合根的Command進行排隊

和上面秒殺的設計一樣,我們可以對要同時修改同一個聚合根的Command進行排隊。只不過這里的排隊不是在MySQL Server端,而是在我們自己程序里做這個排隊。如果我們是單臺服務器處理所有的Command,那排隊很容易做。就是只要在內存中,當要處理某個Command時,判斷當前Command要修改的聚合根是否前面已經有Command在處理,如果有,則排隊;如果沒有,則直接執行。然后當這個聚合根的前一個Command執行完后,我們就能處理該聚合根的下一個Command了;但是如果是集群的情況下呢,也就是你不止有一臺服務器在處理Command,而是有十臺,那要怎么辦呢?因為同一時刻,完全有可能有兩個不同的Command在修改同一個聚合根。這個問題也簡單,就是我們可以對要修改聚合根的Command根據聚合根的ID進行路由,根據聚合根的ID的hashcode,然后和當前處理Command的服務器數目取模,就能確定當前Command要被路由到哪個服務器上處理了。這樣我們能確保在服務器數目不變的情況下,針對同一個聚合根實例修改的所有Command都是被路由到同一臺服務器處理。然后加上我們前面在單個服務器里面內部做的排隊設計,就能最終保證,對同一個聚合根的修改,同一時刻只有一個線程在進行。

通過上面這兩個設計,我們可以確保C端所有的Command,都不會出現并發沖突。但是也要付出代價,那就是要接受最終一致性。比如Saga的思想,就是在最終一致性的基礎上而實現的一種設計。然后,基于以上兩點的這種架構的設計,我覺得最關鍵的是要做到:1)分布式消息隊列的可靠,不能丟消息,否則Saga流程就斷了;2)消息隊列要高性能,支持高吞吐量;這樣才能在高并發時,實現整個系統的整體的高性能。我開發的EQueue就是為了這個目標而設計的一個分布式消息隊列,有興趣的朋友可以去了解下哦。

Command和Event的冪等處理

CQRS架構是基于消息驅動的,所以我們要盡量避免消息的重復消費。否則,可能會導致某個消息被重復消費而導致最終數據無法一致。對于CQRS架構,我覺得主要考慮三個環節的消息冪等處理。

Command的冪等處理

這一點,我想不難理解。比如轉賬的例子中,假如A賬號扣減余額的命令被重復執行了,那會導致A賬號扣了兩次錢。那***就數據無法一致了。所以,我們要保證Command不能被重復執行。那怎么保證呢?想想我們平時一些判斷重復的操作怎么做的?一般有兩個做法:1)db對某一列建唯一索引,這樣可以嚴格保證某一列數據的值不會重復;2)通過程序保證,比如插入前先通過select查詢判斷是否存在,如果不存在,則insert,否則就認為重復;顯然通過第二種設計,在并發的情況下,是不能保證絕對的唯一性的。然后CQRS架構,我認為我們可以通過持久化Command的方式,然后把CommandId作為主鍵,確保Command不會重復。那我們是否要每次執行Command前線判斷該Command是否存在呢?不用。因為出現Command重復的概率很低,一般只有是在我們服務器機器數量變動時才會出現。比如增加了一臺服務器后,會影響到Command的路由,從而最終會導致某個Command會被重復處理,關于這里的細節,我這里不想多展開了,呵呵。有問題到回復里討論吧。這個問題,我們也可以***程度上避免,比如我們可以在某一天系統最空的時候預先增加好服務器,這樣可以把出現重復消費消息的情況降至***。自然也就***化的避免了Command的重復執行。所以,基于這個原因,我們沒有必要在每次執行一個Command時先判斷該Command是否已執行。而是只要在Command執行完之后,直接持久化該Command即可,然后因為db中以CommandId為主鍵,所以如果出現重復,會主鍵重復的異常。我們只要捕獲該異常,然后就知道了該Command已經存在,這就說明該Command之前已經被處理過了,那我們只要忽略該Command即可(當然實際上不能直接忽略,這里我由于篇幅問題,我就不詳細展開了,具體我們可以再討論)。然后,如果持久化沒有問題,說明該Command之前沒有被執行過,那就OK了。這里,還有個問題也不能忽視,就是某個Command***次執行完成了,也持久化成功了,但是它由于某種原因沒有從消息隊列中刪除。所以,當它下次再被執行時,Command Handler里可能會報異常,所以,健壯的做法時,我們要捕獲這個異常。當出現異常時,我們要檢查該Command是否之前已執行過,如果有,就要認為當前Command執行正確,然后要把之前Command產生的事件拿出來做后續的處理。這個問題有點深入了,我暫時不細化了。有興趣的可以找我私聊。

Event持久化的冪等處理

然后,因為我們的架構是基于ES的,所以,針對新增或修改聚合根的Command,總是會產生相應的領域事件(Domain Event)。我們接下來的要做的事情就是要先持久化事件,再分發這些事件給所有的外部事件訂閱者。大家知道,聚合根有生命周期,在它的生命周期里,會經歷各種事件,而事件的發生總有確定的時間順序。所以,為了明確哪個事件先發生,哪個事件后發生,我們可以對每個事件設置一個版本號,即version。聚合根***個產生的事件的version為1,第二個為2,以此類推。然后聚合根本身也有一個版本號,用于記錄當前自己的版本是什么,它每次產生下一個事件時,也能根據自己的版本號推導出下一個要產生的事件的版本號是什么。比如聚合根當前的版本號為5,那下一個事件的版本號則為6。通過為每個事件設計一個版本號,我們就能很方便的實現聚合根產生事件時的并發控制了,因為一個聚合根不可能產生兩個版本號一樣的事件,如果出現這種情況,那說明一定是出現并發沖突了。也就是一定是出現了同一個聚合根同時被兩個Command修改的情況了。所以,要實現事件持久化的冪等處理,也很好做了,就是db中的事件表,對聚合根ID+聚合根當前的version建唯一索引。這樣就能在db層面,確保Event持久化的冪等處理。另外,對于事件的持久化,我們也可以像秒殺那樣,實現Group Commit。就是Command產生的事件不用立馬持久化,而是可以先積累到一定的量,比如50個,然后再一次性Group Commit所有的事件。然后事件持久化完成后,再修改每個聚合根的狀態即可。如果Group Commit事件時遇到并發沖突(由于某個聚合根的事件的版本號有重復),則退回為單個一個個持久化事件即可。為什么可以放心的這樣做?因為我們已經基本做到確保一個聚合根同一時刻只會被一個Command修改。這樣就能基本保證,這些Group Commit的事件也不會出現版本號沖突的情況。所以,大家是否覺得,很多設計其實是一環套一環的。Group Commit何時出發?我覺得可以只要滿足兩個條件了就可以觸發:1)某個定時的周期到了就可以觸發,這個定時周期可以根據自己的業務場景進行配置,比如每隔50ms觸發一次;2)要Commit的事件到達某個***值,即每批可以持久化的事件個數的***值,比如每50個事件為一批,這個BatchSize也需要根據實際業務場景和你的存儲db的性能綜合測試評估來得到一個最適合的值;何時可以使用Group Commit?我覺得只有是在并發非常高,當單個持久化事件遇到性能瓶頸時,才需要使用。否則反而會降低事件持久化的實時性,Group Commit提高的是高并發下單位時間內持久化的事件數。目的是為了降低應用和DB之間交互的次數,從而減少IO的次數。不知不覺就說到了最開始說的那3點性能優化中的,盡量減少IO了,呵呵。

Event消費時的冪等處理

CQRS架構圖中,事件持久化完成后,接下來就是會把這些事件發布出去(發送到分布式消息隊列),給消費者消費了,也就是給所有的Event Handler處理。這些Event Handler可能是更新Q端的ReadDB,也可能是發送郵件,也可能是調用外部系統的接口。作為框架,應該有職責盡量保證一個事件盡量不要被某個Event Handler重復消費,否則,就需要Event Handler自己保證了。這里的冪等處理,我能想到的辦法就是用一張表,存儲某個事件是否被某個Event Handler處理的信息。每次調用Event Handler之前,判斷該Event Handler是否已處理過,如果沒處理過,就處理,處理完后,插入一條記錄到這個表。這個方法相信大家也都很容易想到。如果框架不做這個事情,那Event Handler內部就要自己做好冪等處理。這個思路就是select if not exist, then handle, and at last insert的過程。可以看到這個過程不像前面那兩個過程那樣很嚴謹,因為在并發的情況下,理論上還是會出現重復執行Event Handler的情況。或者即便不是并發時也可能會造成,那就是假如event handler執行成功了,但是last insert失敗了,那框架還是會重試執行event handler。這里,你會很容易想到,為了做這個冪等支持,Event Handler的一次完整執行,需要增加不少時間,從而會***導致Query Side的數據更新的延遲。不過CQRS架構的思想就是Q端的數據由C端通過事件同步過來,所以Q端的更新本身就是有一定的延遲的。這也是CQRS架構所說的要接收最終一致性的原因。

關于冪等處理的性能問題的思考

關于CommandStore的性能瓶頸分析

大家知道,整個CQRS架構中,Command,Event的產生以及處理是非常頻繁的,數據量也是非常大的。那如何保證這幾步冪等處理的高性能呢?對于Command的冪等處理,如果對性能要求不是很高,那我們可以簡單使用關系型DB即可,比如Sql Server, MySQL都可以。要實現冪等處理,只需要把主鍵設計為CommandId即可。其他不需要額外的唯一索引。所以這里的性能瓶頸相當于是對單表做大量insert操作的***TPS。一般MySQL數據庫,SSD硬盤,要達到2W TPS應該沒什么問題。對于這個表,我們基本只有寫入操作,不需要讀取操作。只有是在Command插入遇到主鍵沖突,然后才可能需要偶爾根據主鍵讀取一下已經存在的Command的信息。然后,如果單表數據量太大,那怎么辦,就是分表分庫了。這就是最開始談到的,要避開海量數據這個原則了,我想就是通過sharding避開大數據來實現繞過IO瓶頸的設計了。不過一旦涉及到分庫,分表,就又涉及到根據什么分庫分表了,對于存儲Command的表,我覺得比較簡單,我們可以先根據Command的類型(相當于根據業務做垂直拆分)做***級路由,然后相同Command類型的Command,根據CommandId的hashcode路由(水平拆分)即可。這樣就能解決Command通過關系型DB存儲的性能瓶頸問題。其實我們還可以通過流行的基于key/value的NoSQL來存儲,比如可以選擇本地運行的leveldb,或者支持分布式的ssdb,或者其他的,具體選擇哪個,可以結合自己的業務場景來選擇。總之,Command的存儲可以有很多選擇。

關于EventStore的性能瓶頸分析

通過上面的分析,我們知道Event的存儲唯一需要的是AggregateRootId+Version的唯一索引,其他就無任何要求了。那這樣就和CommandStore一樣好辦了。如果也是采用關系型DB,那只要用AggregateRootId+Version這兩個作為聯合主鍵即可。然后如果要分庫分表,我們可以先根據AggregateRootType做***級垂直拆分,即把不同的聚合根類型產生的事件分開存儲。然后和Command一樣,相同聚合根產生的事件,可以根據AggregateRootId的hashcode來拆分,同一個AggregateRootId的所有事件都放一起。這樣既能保證AggregateRootId+Version的唯一性,又能保證數據的水平拆分。從而讓整個EventStore可以***制水平伸縮。當然,我們也完全可以采用基于key/value的NoSQL來存儲。另外,我們查詢事件,也都是會確定聚合根的類型以及聚合根的ID,所以,這和路由機制一直,不會導致我們無法知道當前要查詢的聚合根的事件在哪個分區上。

設計存儲時的重點考慮點

在設計command, event的存儲時,我認為主要考慮的應該是提高整體的吞吐量,而不是追求單機存儲的性能。因為假如我們的系統平均每秒產生1W個事件,那一天就是8.64億個事件。已經是很大的數據量。所以,我們必須要對command, event這種進行分片。比如我們設計864個表,那每個表每天產生100W條記錄,這是在可以接受的范圍內。然后,我們一旦分了864個表了,肯定會把它們分布在不同的物理數據庫上。這樣就是多個物理數據庫同時提供存儲服務,可以整體提高存儲的吞吐量。我個人比較傾向于使用MySQL來存儲即可,因為一方面MySQL是開源的,各種分庫分表的成熟做法比較多。另一方面,關系型數據庫相比Mongodb這種,自己更熟悉,能更好的控制。比如數據擴容方案可以自己做,不像MongoDB這種,雖然它都幫我們搞定了大數據存儲,但一旦出了問題,也許自己無法掌控。另一方面,關于RT,即單條數據存儲時的響應時間,這個我覺得不管是關系型數據庫還是NoSQL,最終的瓶頸都是在磁盤IO。NoSQL之所以這么快,無非就是異步刷盤;而關系型DB不是很快,因為它要保證數據的落地,要保證數據的更高級別的可靠性。所以,我覺得,要在保證數據不會丟失的情況下,盡量提高RT,可以考慮使用SSD硬盤。另一方面,我覺得由于我們已經做了分庫分表了,所以單個DB的壓力不會太大,所以一般局域網內的RT也不會延遲很大,應該可以接受。

聚合根的內存模式(In-Memory)

In-Memory模式也是一種減少網絡IO的一種設計,通過讓所有生命周期還沒結束的聚合根一直常駐在內存,從而實現當我們要修改某個聚合根時,不必再像傳統的方式那樣,先從db獲取聚合根,再更新,完成后再保存到db了。而是聚合根一直在內存,當Command Handler要修改某個聚合根時,直接從內存拿到該聚合根對象即可,不需要任何序列化反序列化或IO的操作。基于ES模式,我們不需要直接保存聚合根,而是只要簡單的保存聚合根產生的事件即可。當服務器斷電要恢復聚合根時,則只要用事件溯源(Event Sourcing, ES)的方式恢復聚合根到***狀態即可。

了解過actor的人應該也知道actor也是整個集群中就一個實例,然后每個actor自己都有一個mailbox,這個mailbox用于存放當前actor要處理的所有的消息。只要服務器不斷電,那actor就一直存活在內存。所以,In-Memory模式也是actor的一個設計思想之一。像之前很轟動的國外的一個LMAX架構,號稱每秒單機單核可以處理600W訂單,也是完全基于in-memory模式。不過LMAX架構我覺得只要作為學習即可,要大范圍使用,還是有很多問題要解決,老外他們使用這種架構來處理訂單,也是基于特定場景的,并且對編程(代碼質量)和運維的要求都非常高。具體有興趣的可以去搜一下相關資料。

關于in-memory架構,想法是好的,通過將所有數據都放在內存,所有持久化都異步進行。也就是說,內存的數據才是***的,db的數據是異步持久化的,也就是某個時刻,內存中有些數據可能還沒有被持久化到db。當然,如果你說你的程序不需要持久化數據,那另當別論了。那如果是異步持久化,主要的問題就是宕機恢復的問題了。我們看一下akka框架是怎么持久化akka的狀態的吧。

  1. 多個消息同時發送給actor時,全部會先放入該actor的mailbox里排隊;
  2. 然后actor單線程從mailbox順序消費消息;
  3. 消費一個后產生事件;
  4. 持久化事件,akka-persistence也是采用了ES的方式持久化;
  5. 持久化完成后,更新actor的狀態;
  6. 更新狀態完成后,再處理mailbox中的下一個消息;

從上面的過程,我們可以看出,akka框架本質上也實現了避免資源競爭的原則,因為每個actor是單線程處理它的mailbox中的每個消息的,從而就避免了并發沖突。然后我們可以看到akka框架也是先持久化事件之后,再更新actor的狀態的。這說明,akka采用的也叫保守的方式,即必須先確保數據落地,再更新內存,再處理下一個消息。真正理想的in-memory架構,應該是可以忽略持久化,當actor處理完一個消息后,立即修改自己的狀態,然后立即處理下一個消息。然后actor產生的事件的持久化,完全是異步的;也就是不用等待持久化事件完成后再更新actor的狀態,然后處理下一個消息。

我認為,是不是異步持久化不重要,因為既然大家都要面臨一個問題,就是要在宕機后,恢復actor的狀態,那持久化事件是不可避免的。所以,我也是認為,事件不必異步持久化,完全可以像akka框架那樣,產生的事件先同步持久化,完成后再更新actor的狀態即可。這樣做,在宕機恢復actor的狀態到***時,就只要簡單的從db獲取所有事件,然后通過ES得到actor***狀態即可。然后如果擔心事件同步持久化有性能瓶頸,那這個總是不可避免,這塊不做好,那整個系統的性能就上不去,所以我們可以采用SSD,sharding, Group Commit, NoSQL等方法,優化持久化的性能即可。當然,如果采用異步持久化事件的方式,確實能大大提高actor的處理性能。但是要做到這點,還需要有一些前提的。比如要確保整個集群中一個actor只有一個實例,不能有兩個一樣的actor在工作。因為如果出現這種情況,那這兩個一樣的actor就會同時產生事件,導致***事件持久化的時候必定會出現并發沖突(事件版本號相同)的問題。但要保證急群眾一個actor只有一個實例,是很困難的,因為我們可能會動態往集群中增加服務器,此時必定會有一些actor要遷移到新服務器。這個遷移過程也很復雜,一個actor從原來的服務器遷移到新的服務器,意味著要先停止原服務器的actor的工作。然后還要把actor再新服務器上啟動;然后原服務器上的actor的mailbox中的消息還要發給新的actor,然后后續可能還在發給原actor的消息也要轉發到新的actor。然后新的actor重啟也很復雜,因為要確保啟動之后的actor的狀態一定是***的,而我們知道這種純in-memory模式下,事件的持久化時異步的,所以可能還有一些事件還在消息隊列,還沒被持久化。所以重啟actor時還要檢查消息隊列中是否還有未消費的事件。如果還有,就需要等待。否則,我們恢復的actor的狀態就不是***的,這樣就無法保證內存數據是***的這個目的,這樣in-memory也就失去了意義。這些都是麻煩的技術問題。總之,要實現真正的in-memory架構,沒那么容易。當然,如果你說你可以用數據網格之類的產品,無分布式,那也許可行,不過這是另外一種架構了。

上面說了,akka框架的核心工作原理,以及其他一些方面,比如akka會確保一個actor實例在集群中只有一個。這點其實也是和本文說的一樣,也是為了避免資源競爭,包括它的mailbox也是一樣。之前我設計ENode時,沒了解過akka框架,后來我學習后,發現和ENode的思想是如此接近,呵呵。比如:1)都是集群中只有一個聚合根實例;2)都對單個聚合根的操作的Command做排隊處理;3)都采用ES的方式進行狀態持久化;4)都是基于消息驅動的架構。雖然實現方式有所區別,但目的都是相同的。

小結

本文,從CQRS+Event Sourcing的架構出發,結合實現高性能的幾個要注意的點(避開網絡開銷(IO),避開海量數據,避開資源爭奪),分析了這種架構下,我所想到的一些可能的設計。整個架構中,一個Command在被處理時,一般是需要做兩次IO,1)持久化Command;2)持久化事件;當然,這里沒有算上消息的發送和接收的IO。整個架構完全基于消息驅動,所以擁有一個穩定可擴展高性能的分布式消息隊列中間件是比不可少的,EQueue正是在向這個目標努力的一個成果。目前EQueue的TCP通信層,可以做到發送100W消息,在一臺i7 CPU的普通機器上,只需3s;有興趣的同學可以看一下。***,ENode框架就是按照本文中所說的這些設計來實現的,有興趣的朋友歡迎去下載并和我交流哦!

責任編輯:張子龍 來源: 博客園
相關推薦

2021-06-06 12:59:14

實現方式計數

2009-11-17 15:52:37

無線路由器

2022-03-07 06:34:22

CQRS數據庫數據模型

2024-03-18 13:43:20

Linux架構

2023-11-01 11:59:13

2011-07-20 17:31:36

關系型數據庫

2024-11-11 00:38:13

Mypy靜態類型

2012-04-08 14:09:50

小米

2022-09-23 15:01:33

圖片加載代碼

2023-11-01 10:38:46

Linux高性能網絡編程

2023-11-01 11:40:46

Linux高性能網絡編程工具

2023-11-01 10:58:31

系統調用高性能網絡編程Linux

2023-11-01 11:27:10

Linux協程

2018-03-26 09:02:54

MongoDB高可用架構

2021-11-23 09:45:26

架構系統技術

2024-12-25 14:03:03

2023-03-08 07:46:53

面試官優化結構體

2023-11-01 11:51:08

Linux性能優化

2021-02-06 09:40:11

LinuxCPU高性能

2020-12-09 09:21:41

微服務架構數據
點贊
收藏

51CTO技術棧公眾號

日韩av大片| av伦理在线| 久久电影网站中文字幕| 日韩在线观看av| 三级黄色片播放| 超免费在线视频| 久久久蜜桃精品| 91欧美精品成人综合在线观看| 农村黄色一级片| 天天躁日日躁成人字幕aⅴ| 色综合视频一区二区三区高清| 亚洲激情啪啪| 欧美熟妇交换久久久久久分类| 美女久久一区| 久久精品视频导航| 中文在线永久免费观看| 国产91欧美| 亚洲国产视频在线| 婷婷精品国产一区二区三区日韩| 精品国产999久久久免费| 久久成人亚洲| 欧美日韩高清在线观看| 中文字幕在线观看免费高清 | 国产欧美一区二区三区久久人妖| 激情视频在线播放| 激情综合网五月| 欧美成人国产一区二区| 欧美婷婷精品激情| 超碰高清在线| 一区二区三区久久久| 日韩免费av电影| 欧美一级淫片aaaaaa| 免费观看久久久4p| 亚洲91av视频| 日本妇女毛茸茸| 日本电影一区二区| 亚洲美女在线看| 三大队在线观看| 亚洲综合资源| 欧美性做爰猛烈叫床潮| 日韩欧美一区三区| 国产99re66在线视频| 亚洲色欲色欲www| 日韩福利在线| 看电影就来5566av视频在线播放| 丁香亚洲综合激情啪啪综合| 国产日韩亚洲欧美| 一级一级黄色片| 久久久久综合| 欧洲s码亚洲m码精品一区| 69精品久久久| 国产精品v亚洲精品v日韩精品 | 99视频一区| 九九热r在线视频精品| 波多野结衣喷潮| 久久一区91| 自拍偷拍免费精品| 人成免费在线视频| 波多野结衣在线观看一区二区三区 | 日韩乱码一区二区三区| 日韩和欧美一区二区三区| 日韩免费观看av| 午夜影院免费在线观看| 麻豆91精品| 日韩69视频在线观看| 无码人妻精品一区二区50| 久久精品日韩欧美| 国产成人精品视频在线| 伊人久久久久久久久久久久| 日韩电影在线免费看| 国产精品久久久久免费a∨大胸| 中文字幕一区二区三区四区欧美| 日韩一区精品字幕| 国产精品久久久久一区二区| 中文字幕一区二区三区波野结| 日韩成人av影视| 国产欧美一区二区三区久久| 国产富婆一级全黄大片| 成人性视频免费网站| 精品国产一区二区三区麻豆小说 | 亚洲欧美另类国产| 黄免费在线观看| 999视频精品| 久久99久久久久久久噜噜| 国产精品日日夜夜| 美女黄色成人网| 国产一区二区在线免费视频| 精品人妻一区二区三区换脸明星| 国产盗摄精品一区二区三区在线| 国精产品99永久一区一区| 国产在线三区| 亚洲摸摸操操av| 日韩精品―中文字幕| 成人精品国产亚洲| 日韩欧美在线网站| 亚洲综合自拍网| 精品国产一区探花在线观看| 久久精品在线播放| 日韩成人免费在线视频| 日日摸夜夜添夜夜添国产精品 | 国产裸体美女永久免费无遮挡| 久久69国产一区二区蜜臀| www日韩av| 国产私人尤物无码不卡| 亚洲桃色在线一区| 99色精品视频| 国产精品白丝久久av网站| 亚洲国内高清视频| 欧美肥妇bbwbbw| 国产欧美日韩一级| 91夜夜未满十八勿入爽爽影院| 五月婷婷狠狠干| 亚洲欧洲美洲综合色网| 国产二区视频在线播放| 成人在线啊v| 亚洲欧洲自拍偷拍| 久久婷婷一区二区| 青青草成人在线观看| 国产精品一区二区免费看| 无遮挡的视频在线观看| 欧美视频国产精品| 日日夜夜精品视频免费观看| 精品国产91| 韩国日本不卡在线| av加勒比在线| 国产精品色一区二区三区| 国产精品无码人妻一区二区在线 | 欧美日韩一区 二区 三区 久久精品| 久久久久亚洲AV成人网人人小说| 色小子综合网| 国产成人涩涩涩视频在线观看| 高h震动喷水双性1v1| 亚洲欧洲国产日韩| 丝袜制服一区二区三区| 特黄特色欧美大片| 欧美激情中文字幕在线| 国产麻豆91视频| 国产精品每日更新在线播放网址| 欧美亚洲一二三区| 97se亚洲| 欧美激情一区二区三级高清视频| 国产人妖在线播放| 国产精品久久毛片a| 成年人网站大全| 亚洲午夜久久| 69av在线视频| 污污网站免费在线观看| 亚洲成av人片在线观看无码| 国产清纯白嫩初高中在线观看性色| 99re66热这里只有精品8| 国产精品一区二区电影| 97电影在线看视频| 欧美视频在线播放| 欧美丰满美乳xxⅹ高潮www| 香蕉视频成人在线观看| 欧美久久电影| 亚州一区二区三区| 在线精品国产欧美| 伊人影院中文字幕| 国产精品久久久久久久久免费桃花 | 性色一区二区三区| 精品一区二区国产| 九色porny自拍视频在线播放| 亚洲国产另类久久精品| 国产一级做a爱片久久毛片a| 99国产精品国产精品久久| 国产91在线视频观看| 国产91精品对白在线播放| 国产精品久久在线观看| 日本在线播放| 3d动漫精品啪啪| 激情五月婷婷小说| 成人爱爱电影网址| 97国产在线播放| 国产一区二区三区天码| 国产美女被下药99| 1区2区在线观看| 亚洲高清免费观看高清完整版| 中国一级特黄毛片| 中文字幕第一区综合| 91欧美一区二区三区| 影音先锋久久| 日本精品一区二区三区高清 久久 日本精品一区二区三区不卡无字幕 | 亚洲电影成人av99爱色| 日韩一区二区视频在线| 欧美国产精品劲爆| 乳色吐息在线观看| 国产精品一区亚洲| 杨幂一区欧美专区| 成人动态视频| 国产成人精品a视频一区www| 精品自拍一区| 亚洲精品福利视频| 在线视频 91| 亚洲国产精品精华液网站| 91网站免费入口| 国产一区二区三区在线观看免费| 精品久久久久久无码中文野结衣| 九一成人免费视频| 亚洲精品日韩av| 电影网一区二区| 久久久av免费| 免费a级毛片在线观看| 91精品婷婷国产综合久久性色 | 国产成人av一区二区三区不卡| 蜜桃av噜噜一区二区三区小说| wwwjizzjizzcom| 久久不见久久见免费视频7| 成人性教育视频在线观看| 综合久久2023| 欧美日本国产在线| fc2在线中文字幕| 精品欧美黑人一区二区三区| 少妇又紧又色又爽又刺激视频| 亚洲午夜一区二区| 一二三四国产精品| 91丨九色丨黑人外教| 国产九九在线视频| 丝袜美腿中文字幕| 樱花视频在线免费观看| 国产精品一区一区三区| 色综合av综合无码综合网站| 亚洲精品一二三区区别| 秋霞毛片久久久久久久久| 久久久久久久久成人| 国产成+人+综合+亚洲欧洲| 国产桃色电影在线播放| 久久综合九色九九| 在线观看免费黄视频| 日韩毛片在线看| 亚洲精品久久久久久无码色欲四季| 欧美性xxxxx极品少妇| 久久久黄色大片| 无吗不卡中文字幕| 国产一级视频在线| 亚洲天堂免费在线观看视频| 手机av在线不卡| 久久久精品tv| 黄色录像a级片| 国产91高潮流白浆在线麻豆| 亚洲国产欧美另类丝袜| 超碰97人人射妻| 亚洲激情影院| 99久久久精品视频| 亚洲精品成人无限看| 日韩午夜视频在线观看| 牛牛精品成人免费视频| 国产精品夜夜夜一区二区三区尤| 老司机亚洲精品一区二区| 成人黄色av播放免费| 日韩一级视频| 国产在线视频欧美| 欧美激情三区| 成人国产精品免费视频| 国产精品1区在线| 69堂成人精品视频免费| 国产精品一区免费在线| 91精品视频在线| 国产麻豆一区二区三区| 18成人在线| 一区二区三区视频播放| 国产精品免费区二区三区观看 | 精品人妻一区二区三区日产| 成人免费视频一区二区| 亚洲の无码国产の无码步美| 91影院在线免费观看| av网站免费在线播放| 久久久久久久久久久久久久久99| 国产jk精品白丝av在线观看| 国产午夜一区二区三区| 农村老熟妇乱子伦视频| 综合久久综合久久| 久久免费视频播放| 欧美日韩一区二区三区| 波多野结衣一二区| 欧美日韩成人综合在线一区二区| 国产精品无码久久av| 日韩久久久精品| 午夜成人鲁丝片午夜精品| 日韩欧美中文在线| 免费网站在线观看视频| 综合久久久久| www.九色.com| 午夜在线视频观看日韩17c| 最近中文字幕一区二区| 国产裸体歌舞团一区二区| 丰满岳乱妇一区二区| 久久久精品2019中文字幕之3| 超碰人人人人人人人| 亚洲品质自拍视频| 国产成人精品片| 欧美日韩日日摸| 亚洲h视频在线观看| 亚洲精品视频免费| 超碰个人在线| 国产69精品久久久久久| 日韩在线电影| 精品久久久久久亚洲| 欧美在线色图| 国产欧美日韩小视频| 日本麻豆一区二区三区视频| 四川一级毛毛片| 国产色综合一区| 免费在线视频观看| 在线观看中文字幕不卡| 午夜老司机福利| 中文字幕视频在线免费欧美日韩综合在线看| 麻豆最新免费在线视频| 91国产高清在线| 国产成人免费av一区二区午夜 | 亚洲精品成人在线播放| 99久久婷婷国产综合精品 | 91欧美日韩| 自慰无码一区二区三区| 国产一区二区美女诱惑| 实拍女处破www免费看| 亚洲黄色av一区| 中文文字幕一区二区三三| 亚洲国产精品福利| h片在线免费| 国产精品久久久久久久久久久久 | 精品美女被调教视频大全网站| 国产黄在线播放| 性欧美视频videos6一9| 久久久久亚洲精品中文字幕| 色99中文字幕| 国产日韩欧美| 亚洲av无码一区东京热久久| 日韩一区欧美小说| 91麻豆精品在线| 亚洲老头老太hd| 1234区中文字幕在线观看| 91免费的视频在线播放| 999国产精品视频| 欧美黑人又粗又大又爽免费| 91麻豆国产精品久久| 男人天堂中文字幕| 日韩欧美一级片| 青青青青在线| 国产精品自拍偷拍视频| 国产九一精品| 国产精品亚洲αv天堂无码| av在线这里只有精品| 日本三级黄色大片| 亚洲成人av资源网| av色在线观看| 久久久水蜜桃| 免费在线亚洲| 成人片黄网站色大片免费毛片| 欧美日韩国产页| 日本国产在线| 国产成人亚洲精品| 日韩理论在线| 99日在线视频| 亚洲精选视频免费看| www.蜜桃av.com| 久久久久久久久久久av| 另类在线视频| 日韩中文字幕组| 国产精品三级在线观看| 91中文字幕在线播放| 久久九九免费视频| 视频在线一区| 男女猛烈激情xx00免费视频| jizz一区二区| 不卡av电影在线| 日韩中文字幕第一页| 不卡一区视频| 日韩 欧美 视频| 久久青草欧美一区二区三区| 中国一级特黄视频| 久久国产精品网站| 欧美1区2区3区4区| 天堂社区在线视频| 亚洲免费毛片网站| 午夜一区在线观看| 国产精品久久久久免费a∨| 欧美一区精品| 国产又爽又黄无码无遮挡在线观看| 日本二三区不卡| 黄色av电影在线播放| 国内一区在线| 日本成人中文字幕在线视频| 成人在线观看小视频| 欧美成人伊人久久综合网| 日本免费在线视频| 国产精品久久在线观看| 欧美伊人久久| 人人妻人人藻人人爽欧美一区| 色域天天综合网| 成人免费观看视频大全| 国产精品区一区二区三含羞草| 久久久成人网| av黄色免费在线观看| 亚洲第一av网站| 电影亚洲精品噜噜在线观看| 国产一级大片免费看| 97久久久精品综合88久久| 国产污污视频在线观看 | 99免在线观看免费视频高清|