对基于消息队列的Activiti异步执行器进行基准测试
一點歷史
永不停息??的一件事是,Activiti如何以驚人的規模在一些大型組織中使用。 過去,這導致了各種優化和重構,其中包括異步執行器-替換舊的作業執行器。 對于未啟動的用戶:這些執行器在流程實例中處理計時器和異步繼續。 特別是在過去的兩年中,我們已經看到它的使用大大增加。 異步執行器的引入極大地提高了性能。 但是,去年在巴黎舉行的社區活動中,我們了解到,在處理大量工作時,執行者使用的查詢可能會導致需要進行表掃描。 這永遠不是一件好事。
因此,我們知道在完成版本6之前,我們確實要做一件事,那就是重構異步執行器,使它使用的所有查詢都變得非常簡單。 這確實意味著我們必須將作業數據拆分為與不同類型和狀態匹配的各種表,同時仍要使API與以前的Activiti版本兼容。
在過去的幾個月中,我們一直在做這些事情(包括許多其他事情),并取得了一些不錯的結果和一些新的不錯的API,它們豐富了該平臺。 我可以在“新的”異步執行程序的工作方式上寫另一個博客,但是昨天我已經完成了文檔工作,所以,如果您對所有工作方式都感興趣, 請查看在線文檔或檢查工具 欄上 的源代碼。 v6分支 。
架構設計當然受我們從過去兩種實現中學到的知識的影響,但同時也受消息隊列系統中概念的影響很大。 設計目標之一是,插入消息隊列并運行它應該非常容易,因為我們有一種直覺,認為這將對性能有所幫助。
因此,我們做到了。 由于新的體系結構,使異步執行程序與消息隊列一起工作幾乎是微不足道的。 如果您對實現感興趣, 我還會在文檔中添加有關此主題的部分。
而且,當然,您知道我,我只是想將這兩個執行程序實現相互比較基準��
基準項目
您可以找到我在Github上使用的代碼: https : //github.com/jbarrez/queue-based-async-executor-benchmark
基本上,它的工作是使用配置屬性文件運行Main.java。
- 使用適當的配置啟動流程引擎(我最近在網上看到一些Activiti基準測試,這些基準測試了Activiti的性能而未使用適當的連接池數據源。很遺憾,但是無論如何。)
- 如果以“生產者”身份運行,將啟動10000個流程實例,每10毫秒一個。 定期的統計信息將被打印到控制臺上。
- 如果以“執行程序”身份運行,則將流程引擎配置為啟用異步執行程序。
- 可以有任意數量的生產者/執行者,但是所有生產者/執行者都進入同一個數據庫。
項目中使用的流程定義如下:
需要注意的重要一點(在圖表上不可見)是,在這個重要的流程定義中,所有服務任務都是異步的。 并行派生之后的服務任務與加入的并行網關一樣配置為互斥的 。 這里有兩個計時器,其中用戶任務上的一個是1秒,子流程上的一個是50分鐘。 總而言之,當啟動流程實例時,它導致需要執行27個作業才能到達終點。 對于10000個實例,這意味著我們正在有效測試270 000個作業的吞吐量。
請注意,與任何基準測試一樣,原始數字說明了一切,但不是全部。 這一切都取決于服務器硬件,實際的流程定義和許多其他細節。 但是,如果在完全相同的硬件上執行完全相同的代碼,相對數字確實會給我們帶來很多啟發。 閱讀下一部分時,請記住這一點。
測試環境
所有基準測試都是在Amazon Web Services(AWS)上運行的,生產者/執行者使用EC2服務器,r3.4xlarge(16個vCPU,16個vCPU)上的數據庫使用RDS PostgresQL (因為Postgres是一個很棒的數據庫,非常容易設置) 122 GiB內存)。
使用以下EC2配置
- RDS(postgres):r3.4xlarge(16個vCPU,122 GiB內存)
- 生產引擎:c3.4xlarge(16個vCPU,30 GiB內存)
- 執行器引擎:c3.8xlarge(32個vCPU,60 GiB內存)
所有的服務器都在歐盟西部地區運行。 因此,所有測試結果都具有真實的網絡延遲(沒有運行在localhost基準測試上的延遲,因此跳過了網上經常看到的聯網)。 當運行上面的項目時,JVM分配了8GB的空間。
我們將使用的指標是作業的吞吐量 ,以作業/秒表示。 簡而言之,在測試運行之后,我們驗證數據庫中的數據是否正確(即10K完成的流程實例),并采用第一個開始時間和最后一個結束時間,這使我們獲得了x秒的時間。 則吞吐量為x / 270000(我們知道每個流程實例等于27個作業)。
基線測量
基準測試的第一件事是“基準”,即由線程池支持的常規異步執行程序(即v5中異步執行程序的改進設計)。 在此測試中,我們使用了2臺服務器,并進行了以下配置(注意:6.0.0.Beta3實際上是快照版本):
| 一個 | 乙 | C | d | |
| Activiti版本 | 6.0.0.Beta3 | 6.0.0.Beta3 | 6.0.0.Beta3 | 5.21.0 | 
| 生產者引擎 | 1個 | 1個 | 1個 | 1個 | 
| 執行器引擎 | 1個 | 1個 | 2 | 2 | 
| #池中的線程 | 32 | 10 | 10 | 10 | 
| 阻塞隊列大小 | 256 | 100 | 100 | 100 | 
一些有趣的觀察:
我認為配置A會比配置B更好,因為這臺機器畢竟有32個CPU,因此將線程池的線程數與此匹配是有意義的。 但是,配置B的設置非常相似,除了只有10個線程和較小的阻塞隊列之外,它的性能大大提高(310 vs 210作業/秒)。 一個可能的解釋可能是32個線程爭用太多? 我確實記得當初選擇默認值“ 10”時,我們進行了一些基準測試,其中10是吞吐量最佳的“魔術數”(但我確實認為這取決于所使用的機器。
我希望添加另一個執行程序節點會產生更大的影響,畢竟我們要添加32個CPU的計算機,但是收益很小(310到326)。 我們將學習原因,并在本文的稍后階段進行修復。
使用Activiti版本5.21.0的配置D使用與配置C相同的設置。但是,改進的版本6的異步執行程序顯然在這里勝出(326 vs 266)。 這當然是我們希望的:-)。
到目前為止,我們最好的結果是每秒326個作業 (并使用兩臺服務器)。
基準線的變化
鑒于以上設置,可以詢問運行混合的生產者/執行者時產生的影響。 這是Activiti引擎默認的運行方式:引擎將同時負責啟動流程實例并立即執行它們。 這是配置E (與配置C相同,除了兩個引擎現在都是生產者/執行者),結果如下所示。 而且顯然性能較差。 一種解釋可能是機器已經每10毫秒使用10個線程來啟動流程實例,這可能導致與異步執行器的10個線程進行相當多的爭用。 可能可以對該設置進行很多調整以獲得更好的數字,但這不是此博客的目標。 但是結果仍然很有趣。
因此,考慮到兩個執行器引擎勝于一個執行器引擎,合乎邏輯的事情是嘗試三個執行器。 這是配置F。
類似于從一個執行程序到兩個執行程序,吞吐量提高了。 但不是以一種壯觀的線性方式。
介紹基于消息隊列的異步執行器
現在該切換到基于消息隊列的異步執行器了,現在我們有了基準編號。 我選擇了最新版本的ActiveMQ ,因為我對此很熟悉,并且設置起來非常容易。 我沒有花時間調整ActiveMQ,切換持久性策略或嘗試替代方法。 因此,那里也可能會有一些利潤。
在基準項目中,我將Spring與以下配置一起使用: https : //github.com/jbarrez/queue-based-async-executor-benchmark/blob/master/src/main/java/org/activiti/MyConfigMessageExecutor.java 。 之所以選擇Spring,是因為MessageListenerContainer提供了一種簡單的方法來使消息隊列偵聽器可以很好地與多個線程一起工作(否則,JBoss這樣的應用程序服務器會為您提供)。 更具體地說,MessageListenerContainer的concurrenConsumers設置允許設置用于以智能方式監聽消息的線程數。 是的,該類確實具有很多可能會更好地影響結果的屬性,但這又不是重點。 請記住相對數字。
對于此配置,我們使用與config C類似的設置(到目前為止,我們在兩臺服務器上的最佳結果),稱為config G:1個生產者引擎,2個執行者引擎。 請注意,我們現在還在混合中添加了“隊列服務器”,它使用的是c3.8xlarge機器(32個vCPU,60 GiB RAM),類似于執行引擎服務器。
結果低于…,它們簡直太棒了:在等效設置(但帶有額外的消息隊列服務器)中的消息隊列異步執行程序比基于線程池的異步執行程序快四倍 。
一個小的實現說明:我們不得不切換到UUID ID生成器 ,因為吞吐量對于默認值而言太高了。 請記住,UUID生成器比默認生成器慢,結果甚至更棒(因為我們在這里真正談論的是毫秒)。
有趣的觀察!
如果您運行基準測試項目,則會看到它定期吐出一些統計信息,因此您可以跟蹤系統中有多少個作業,計時器,用戶任務,歷史活動實例,流程實例等。
在運行消息隊列設置時,從這些數字中可以很清楚地看出一種模式。 基于線程池的異步執行器可以更快地完成流程實例(例如,大約1分鐘后,我們看到一批流程實例已完成),而對于基于消息的異步執行器,流程實例實際上都在一個大的突發中完成了。 這表明后者將更多地分散流程實例活動的執行,而基于線程的活動將繼續進行直到完成為止。
團隊中的一些討論導致了對此的解釋:基于線程池的線程將始終將下一個異步作業傳遞給執行程序,而基于消息的線程將其放在隊列中,在隊列中已經有數千條消息正在等待。 現在添加一個事實,即對于流程實例,我們有很多排它異步作業,這意味著對于基于線程池的異步作業,許多線程試圖獲取流程實例鎖,但由于正在執行排他實例而失敗。 但是,這項工作沒有獲得 ,很快就重新開始了。 對于基于消息隊列的消息隊列,將它們再次添加到消息隊列的末尾。 其中有數千條其他消息正在等待。 回到執行此特定消息時,排他鎖很可能已經很久了。
這導致在基于線程池的異步執行程序中進行一些重構:刪除并重新插入作業,而不是簡單地釋放作業的鎖定,從而有效地模擬了隊列行為。 這是修復程序: https : //github.com/Activiti/Activiti/commit/d08a247570336c872bb17ce513c1fb95b3ba47a2#diff-bd9c7efdb4c57462f6fe71641b280942R212 。
在與config C完全相同的設置(稱為config H(1個生產者,2個執行程序))中對這些基準進行基準測試,這表明我們此簡單的解決方案將吞吐量提高了34%! 現在我們有了一個新的基準
更好的消息隊列異步執行器結果
因此,在消息隊列結果(配置G)中,我們使用了10個線程的相當保守的設置來偵聽消息。 想法是我們也有10個線程用于線程池。 當然,消息隊列使用者從根本上不同于輪詢線程:此類使用者與隊列具有持久連接,而隊列代理實際上將工作推給其使用者。 這應該更有效。 因此,我們嘗試了以下配置,在這些配置中,我們改變了使用者(從而消耗了線程)和執行程序節點的數量。
| 一世 | ? | ? | 大號 | |
| 生產者引擎 | 1個 | 1個 | 1個 | 1個 | 
| 執行器引擎 | 2 | 2 | 3 | 3 | 
| #消費者/引擎 | 32 | 64 | 32 | 64 | 
因此,一個不錯的觀察結果是,添加更多的消費者是超級有效的。 我們正在達到2222.9作業/秒的吞吐量 。 如果您問我,那是非常快的,并且是基于線程池的異步執行器的五倍。
可悲的是,添加更多的執行器機器實際上對性能不利。 我認為瓶頸現在已成為數據庫,以及它如何處理大規模進行的所有并發。 當然,我根本沒有調整數據庫 ,只是常規的RDS postgres實例。 或嘗試使用Aurora或Oracle(在我以前的基準測試中獲得了最好的結果)。 但是,這里的重點是相對數量 ,而不是擠出吞吐量的最后一部分。 我認為相對數字點已經確定為��
結論
數字說明了一切:基于新消息隊列的異步執行器擊敗了基于線程池的異步執行器。 這是否意味著您必須立即切換? 不, 常規的異步執行器也非常快(436作業/秒仍然很快),但是更重要的是,設置非常簡單,因為Activiti引擎可以處理所有事情。 在項目中添加消息隊列意味著額外的復雜性:可能會失敗或崩潰的另一件事,是額外的監視,維護等。但是,當您執行大量 (我的意思是“很多”)異步工作時,您會重新達到默認異步執行器可以執行的操作的限制,很高興知道還有替代方法。
我們還要忘記這里得出的另一個結論:版本6中的新異步執行程序實現是對版本5的重大改進!
進一步的工作
當前的實現僅是Spring / JMS。 但是,該實現對于移植到其他系統和/或協議(應用程序服務器,STOMP,AMPQ,AWS SQS等)而言是微不足道的。 對于將成為流行的下一個選擇的反饋表示贊賞。
有趣的是,這種基于消息隊列的異步執行器使實現“優先級隊列”非常簡單。 優先級隊列是我們許多大型用戶所要求的功能:提供特定的流程定義/實例/在特定條件下/…優先級與常規作業相比。 容易想象如何設置多個隊列和/或分配更少或更多的使用者以優先使用某些用例。
翻譯自: https://www.javacodegeeks.com/2016/07/benchmarking-message-queue-based-activiti-async-executor.html
總結
以上是生活随笔為你收集整理的对基于消息队列的Activiti异步执行器进行基准测试的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: superhot预告片下载_预告片:裸指
- 下一篇: adf开发_ADF:动态视图对象
