2011-11-22 33 views
9

我一直JOliver的事件存儲3.0作爲試驗項目中的一個潛在的組成部分,並一直試圖通過測量事件存儲事件的吞吐量。事件商店3.0 - 吞吐量/性能

我開始使用一個簡單的線束,它基本上遍歷了一個for循環,創建一個新的流並將一個包含GUID ID和一個字符串屬性的非常簡單的事件提交給MSSQL2K8 R2 DB。調度員本質上是沒有操作的。

這種方法成功地實現〜3K操作/秒在與DB的8路HP G6 DL380在一個單獨的32路G7 DL580運行。測試機器沒有資源限制,在我的情況下阻塞看起來是極限。

有沒有人有測量事件存儲的吞吐量和什麼樣的人物的經驗已經實現?我希望獲得至少1個數量級的吞吐量,以使其成爲可行的選擇。

回答

6

我同意阻止IO將成爲最大的瓶頸。我可以在基準測試中看到的其中一個問題是您正在針對單個流進行操作。您的域中有多少個聚合根,每秒3K +事件? EventStore的主要設計是針對多個聚合的多線程操作,可減少讀取應用程序的爭用和鎖定。

此外,你使用什麼序列化機制? JSON.NET?我還沒有Protocol Buffers實現,但是每個基準都表明PB在性能方面明顯更快。對你的應用程序運行一個分析器來查看最大的瓶頸是什麼會很有趣。

我注意到另一件事是,你推出一個網絡躍到這會增加延遲方程(和封鎖時間)對任何單個流。如果您正在寫入使用固態驅動器的本地SQL實例,與運行磁盤驅動器的遠程SQL實例相比,數據數量要高得多,並且數據和日誌文件在同一張盤片上。

最後,沒有你的基準測試應用程序使用System.Transactions的還是沒它默認是沒有交易? (EventStore在不使用System.Transactions或任何類型的SQL事務的情況下是安全的。)現在,儘管如此,我毫不懷疑EventStore中的某些區域可以使用一點關注。事實上,我正在爲3.1版本開發幾個向後兼容的模式修訂版,以減少在單個提交操作期間在SQL Server(以及一般的RDBMS引擎)內執行的寫入數量。

一對,可作爲3.X的基礎上改寫2.x的開始,當我面臨的最大設計問題是異步的想法,非阻塞IO。我們都知道node.js和其他非阻塞的web服務器在一個數量級上擊敗了線程化的web服務器。然而,調用者引入的複雜性的可能性增加了,這是必須強調的,因爲它是大多數程序和圖書館運行方式的根本轉變。如果並且當我們確實轉向了一個平滑的非阻塞模型時,它會在4.x時間框架中更多。

底線:發佈您的基準,以便我們可以看到瓶頸在哪裏。

+1

感謝Jonathan的回覆。澄清;)每個提交是一個新的EventSource,所以我每秒提交3K個不同的EventSources。收集網絡跳並沒有改善,但是一個有效的點。就交易而言,我並不明確參與交易,但這可能與不使用交易不同。我使用JSON進行序列化,但由於我們不受CPU限制,我不認爲這會限制我們。我已經發布了測試工具到GitHub(https://github.com/MattCollinge/EventStore-Performance-Tests.git)。 – MattC

6

優秀的問題馬特(+1),我看到奧利弗先生本人回答爲答案(+1)!

我想拋出一種稍微不同的方法,我自己在玩,以幫助解決您遇到的每秒3000次提交瓶頸問題。

CQRS模式,大多數使用JOliver的EventStore的人似乎都試圖遵循CQRS模式,允許大量的「擴展」子模式。通常排隊的第一個人是Event提交自己,這是你看到的一個瓶頸。「Queue off」表示從實際提交中卸載並將它們插入一些寫入優化的非阻塞I/O進程或「隊列」。

我不嚴謹的解釋是:

命令廣播 - >命令處理程序 - >事件廣播 - >事件處理程序 - >事件商店

實際上有兩個向外擴展點這裏在這些模式:在命令處理程序事件處理程序。如上所述,大多數情況下從擴展事件處理程序部分開始,或者將您的情況提交到EventStore庫,因爲這通常是由於需要將其保存在某處(例如Microsoft SQL Server數據庫)而導致的最大瓶頸。

我自己正在使用幾個不同的提供程序來測試最佳性能來「排隊」這些提交。 CouchDB和.NET的AppFabric Cache(它具有很好的GetAndLock()功能)。 [OT]我非常喜歡AppFabric的持久緩存功能,它可以讓您創建冗餘緩存服務器,在多臺機器上備份您的區域 - 因此,只要至少有一臺服務器啓動並運行,緩存就會保持運行狀態。[/ OT]

所以,想象你的事件處理程序不直接寫入提交到EventStore。相反,你有一個處理程序將它們插入到一個「隊列」系統中,比如Windows Azure Queue,CouchDB,Memcache,AppFabric Cache等。關鍵是選擇一個只有很少或沒有塊的系統來排隊事件,但是這是持久的內置冗餘(Memcache是​​我最不喜歡的冗餘選項)。你必須有這種冗餘,如果服務器掉線,你仍然有事件排隊。

要最終從此「排隊事件」提交,有幾個選項。我喜歡Windows Azure的隊列模式,因爲許多「工作人員」可以不斷地在隊列中尋找工作。但它不一定是Windows Azure - 我使用在後臺線程中運行的「隊列」和「輔助角色」模仿本地代碼中的Azure隊列模式。它非常好地縮放。假設你有10名工作人員不斷查看任何用戶更新事件(我通常爲每個事件類型編寫一個單一的輔助角色,在監控每種類型的統計信息時使伸縮更容易),不斷查看此「隊列」。將兩個事件插入隊列中,前兩名工作人員立即每次接收一條消息,並將它們(提交他們)直接插入到EventStore中 - 多線程,就像喬納森在他的答案中提到的那樣。您使用該模式的瓶頸將是您選擇的任何數據庫/事件存儲支持。假設你的EventStore使用的是MSSQL,瓶頸仍然是3000 RPS。這很好,因爲當這些RPS下降到20000突發之後的50 RPS時,系統就會「趕上」。這是CQRS允許的自然模式:「最終一致性」。

我說有其他的向外擴展模式是CQRS模式原生的。另一個,正如我上面提到的,是命令處理程序(或命令事件)。這也是我所做的一件事,特別是如果你有一個非常豐富的域名,就像我的一個客戶那樣(在每個命令上有幾十個處理器密集的驗證檢查)。在這種情況下,我實際上會排隊執行自己的命令,並由一些工作角色在後臺處理。這也給你一個很好的橫向擴展模式,因爲現在你的整個後端,包括事件的EvetnStore提交,都可以進行線程化。

很顯然,這樣做的缺點是您會放鬆一些實時驗證檢查。我通過在構建域時通常將驗證分爲兩類來解決這個問題。一個是Ajax或者域中的實時「輕量級」驗證(類似於Pre-Command檢查)。其他則是嚴重失效的驗證檢查,只能在域中完成,但不能用於實時檢查。然後,您需要在Domain模型中針對失敗進行編碼。意思是,如果某件事失敗,則總是編碼出路,通常以通知電子郵件的形式返回給用戶,說明出現了問題。由於該用戶不再被該隊列中的命令阻止,因此如果該命令失敗,則需要通知他們。

而你需要進入'後端'的驗證檢查將進入你的查詢或「只讀」數據庫,riiiight?不要進入EventStore檢查唯一的電子郵件地址。您將針對您的前端查詢的高可用性只讀數據存儲庫進行驗證。嘿,只有一個CouchDB文檔專用於系統中所有電子郵件地址的列表,作爲CQRS的查詢部分。

CQRS只是建議......如果您確實需要實時檢查繁重的驗證方法,那麼您可以在此前建立一個Query(只讀)存儲庫,並在PreCommand階段加快驗證速度它被插入到隊列中。很多的靈活性。我甚至會爭辯說,驗證諸如空的用戶名和空電子郵件等事情甚至不是域問題,而是UI負責(減輕了在域中進行實時驗證的需要)。我已經構建了一些項目,我在MVC/MVVM ViewModels上進行了非常豐富的UI驗證。當然,我的域有非常嚴格的驗證,以確保它在處理之前有效。但是,將平庸的輸入驗證檢查或我稱之爲「輕量級」驗證的內容移到ViewModel圖層中,可以向最終用戶提供近乎即時的反饋,而無需進入我的域。 (有一些技巧可以保持與您的域同步)。

因此,總之,可能要在提交這些事件之前仔細研究這些事件。正如喬納森在他的回答中提到的,這很適合EventStore的多線程功能。

+1

有趣的答案。感謝您撰寫! –

0

我們使用Erlang/Elixir,https://github.com/work-capital/elixir-cqrs-eventsourcing使用Eventstore爲大規模併發構建了一個小型樣板。我們仍然需要優化數據庫連接,池化等等,但是對於每個具有多個數據庫連接的聚合進行一個進程的想法與您的需求保持一致。