Activiti绩效对决
到現(xiàn)在為止,當(dāng)您問我同樣的問題時,我將告訴您Activiti如何以各種可能的方式最小化數(shù)據(jù)庫訪問,如何將流程結(jié)構(gòu)分解為“執(zhí)行樹”,以便進(jìn)行快速查詢或如何利用十年的工作流框架開發(fā)知識。
您知道,嘗試不回答問題就解決這個問題。 我們知道它很快,因?yàn)槲覀円呀?jīng)在此基礎(chǔ)上建立了理論基礎(chǔ)。 但是現(xiàn)在我們有了證明:實(shí)數(shù)……。 是的,這將是一個冗長的帖子。 但是請相信我,這值得您花時間!
免責(zé)聲明:性能基準(zhǔn)很難。 真的很難。 不同的機(jī)器,稍有不同的測試設(shè)置…很小的東西會嚴(yán)重改變結(jié)果。 此處的數(shù)字僅是為了證明Activiti引擎的開銷非常小,同時還非常容易集成到Java生態(tài)系統(tǒng)中并提供BPMN 2.0流程執(zhí)行。
Activiti基準(zhǔn)項(xiàng)目
為了測試Activiti引擎的流程執(zhí)行開銷,我在github上創(chuàng)建了一個小項(xiàng)目: https : //github.com/jbarrez/activiti-benchmark
該項(xiàng)目目前包含9個測試過程,我們將在下面進(jìn)行分析。 項(xiàng)目中的邏輯非常簡單:
- 為每次測試運(yùn)行創(chuàng)建一個流程引擎
- 每個進(jìn)程都使用1到10個線程的線程池在此進(jìn)程引擎上順序執(zhí)行。
- 將所有過程都放入一個包中,其中抽取了許多隨機(jī)執(zhí)行的內(nèi)容。
- 收集所有結(jié)果,并生成帶有一些漂亮圖表HTML報(bào)告
要運(yùn)行基準(zhǔn)測試,只需按照github頁上的說明來構(gòu)建和執(zhí)行jar。
基準(zhǔn)結(jié)果
我用于測試結(jié)果的測試計(jì)算機(jī)是我的(相當(dāng)舊的)臺式計(jì)算機(jī):AMD Phenom II X4 940 3.0Ghz,8 Gb 800Mhz RAM和運(yùn)行Ubuntu 11.10的舊版本7200 rpm HD。 用于測試的數(shù)據(jù)庫在運(yùn)行測試的同一臺計(jì)算機(jī)上運(yùn)行。 因此請記住,在“真實(shí)”服務(wù)器環(huán)境中,結(jié)果甚至可能更好!
我上面提到的基準(zhǔn)測試項(xiàng)目是在默認(rèn)的Ubuntu MySQL 5數(shù)據(jù)庫上執(zhí)行的。 我只是切換到'large.cnf'設(shè)置(這會在數(shù)據(jù)庫上拋出更多的RAM以及類似的東西),而不是默認(rèn)配置。
- 每個測試過程使用一個從1個到10個線程的線程池運(yùn)行2500次 。 用simpleton語言:僅使用一個線程執(zhí)行2500個進(jìn)程執(zhí)行,使用兩個線程執(zhí)行2500個線程執(zhí)行,使用三個線程執(zhí)行2500個進(jìn)程執(zhí)行…是的,您知道了。
- 每次基準(zhǔn)測試都是使用“默認(rèn)” Activiti流程引擎完成的。 這基本上意味著以純Java創(chuàng)建的“常規(guī)”獨(dú)立Activiti引擎。 每個基準(zhǔn)測試運(yùn)行都在“ Spring”配置中完成。 在這里,流程引擎是通過將其包裝在工廠bean中而構(gòu)造的,數(shù)據(jù)源是Spring數(shù)據(jù)源,事務(wù)和連接池也由Spring管理(我實(shí)際上是使用經(jīng)過調(diào)整的BoneCP線程池)
- 每次基準(zhǔn)測試運(yùn)行都是在默認(rèn)歷史記錄級別(即“審核”)上使用歷史記錄,并且未啟用歷史記錄(即歷史記錄級別“無”)時執(zhí)行的 。
以下各節(jié)詳細(xì)分析了這些過程,但是這里已經(jīng)是測試運(yùn)行的整體結(jié)果:
- Activiti 5.9 – MySQL –默認(rèn)–歷史記錄已啟用
- Activiti 5.9 – MySQL –默認(rèn)–歷史記錄已禁用
- Activiti 5.9 – MySQL – Spring –歷史記錄已啟用
- Activiti 5.9 – MySQL – Spring –歷史記錄已禁用
我使用Activiti的最新公共發(fā)行版Activiti 5.9運(yùn)行了所有測試。 但是,我的測試運(yùn)行為表面帶來了一些潛在的性能修復(fù)(我還通過探查器運(yùn)行了基準(zhǔn)項(xiàng)目)。 很快就可以清楚地看到,大多數(shù)流程執(zhí)行時間實(shí)際上是在流程結(jié)束時完成的。 基本上,觸發(fā)更多的查詢是不必要的,如果我們要在執(zhí)行樹中保存更多狀態(tài),則沒有必要。 我與來自Camunda的 Daniel Meyer和我的同事Frederik Heremans坐在一起,他們已經(jīng)設(shè)法為此進(jìn)行了修復(fù)! 因此, 當(dāng)前的Activiti 主干 (即Activiti 5.10-SNAPSHOT)比5.9快得多 。
- Activiti 5.10 – MySQL –默認(rèn)–歷史記錄已啟用
- Activiti 5.10 – MySQL –默認(rèn)–歷史記錄已禁用
- Activiti 5.10 – MySQL – Spring –歷史記錄已啟用
- Activiti 5.10 – MySQL – Spring –歷史記錄已禁用
從高級角度(向下滾動以進(jìn)行詳細(xì)分析),需要注意以下幾點(diǎn):
- 由于使用了更多的“專業(yè)”連接池,我期望默認(rèn)配置和Spring配置之間會有一些差異。 但是,兩種環(huán)境的結(jié)果都差不多。 有時默認(rèn)值更快,有時是Spring。 很難真正找到一種模式。 因此,我在下面的詳細(xì)分析中省略了Spring結(jié)果。
- 最佳的平均時間是使用四個線程執(zhí)行進(jìn)程時大多數(shù)時候找到的時間 。 這可能是由于擁有四核計(jì)算機(jī)。
- 當(dāng)使用八個線程執(zhí)行進(jìn)程時,通常會找到最佳的吞吐量數(shù)字。 我只能假設(shè)這與擁有四核計(jì)算機(jī)有關(guān)。
- 當(dāng)線程池中的線程數(shù)增加時,吞吐量(每秒執(zhí)行的進(jìn)程)增加,這兩者均對平均時間產(chǎn)生負(fù)面影響。 當(dāng)然,具有六個或七個以上的線程,您會非常清楚地看到這種效果。 這基本上意味著,雖然進(jìn)程本身需要花費(fèi)更長的時間來執(zhí)行,但是由于有多個線程,您可以在相同的時間內(nèi)執(zhí)行更多這些“較慢”的進(jìn)程。
- 啟用歷史記錄確實(shí)會產(chǎn)生影響。 通常,啟用歷史記錄會使執(zhí)行時間加倍。 這是合乎邏輯的,因?yàn)楫?dāng)歷史記錄處于默認(rèn)級別(即“審核”)時會插入許多額外的記錄。
出于好奇,我進(jìn)行了最后一個測試:在Oracle XE 11.2數(shù)據(jù)庫上運(yùn)行性能最佳的設(shè)置。 Oracle XE是“真實(shí)” Oracle數(shù)據(jù)庫的免費(fèi)版本。 無論多么努力,我都嘗試過,無法在Ubuntu上正常運(yùn)行。 因此,我在同一臺計(jì)算機(jī)上使用了舊的Windows XP安裝。 但是,操作系統(tǒng)是32位,這意味著系統(tǒng)僅具有3.2可用的8Gb RAM。 結(jié)果如下:
- Activiti 5.10 – Windows上的Oracle –默認(rèn)–歷史記錄已禁用
結(jié)果不言而喻。 Oracle吹走了MySQL上的任何(單線程)結(jié)果 (它們已經(jīng)非常快了!)。 但是,當(dāng)使用多線程時,它比任何MySQL結(jié)果都要差得多。 我的猜測是,這些是由于XE版本的局限性 :僅使用一個CPU,僅使用1 GB RAM,等等。 我真的想在一個由Oracle托管的真實(shí)Oracle上運(yùn)行這些測試, DBA…如果您有興趣,請隨時與我聯(lián)系 !
在下一部分中,我們將詳細(xì)研究每個測試過程的性能數(shù)字。 可以自行下載包含以下所有數(shù)字和圖表的Excel工作表。
流程1:裸手(一筆交易)
第一個過程至少在業(yè)務(wù)方面不是一個非常有趣的過程。 開始該過程后,立即結(jié)束。 它本身不是很有用,但是它的數(shù)目使我們了解到一件重要的事情:Activiti引擎的光禿禿的開銷。 以下是平均時間:
此過程在單個事務(wù)中運(yùn)行,這意味著由于Activiti的優(yōu)化禁用了歷史記錄后,沒有任何內(nèi)容保存到數(shù)據(jù)庫中。 啟用歷史記錄后,您基本上將獲得在歷史流程實(shí)例表中插入一行的成本,這大約是4.44毫秒。 同樣很明顯,我們對Activiti 5.10的修復(fù)在這里產(chǎn)生了巨大的影響。 在以前的版本中,有99%的時間用于流程的清理檢查。 在這里查看最佳結(jié)果:使用4個線程執(zhí)行2500次此過程時,為0.47 ms 。 僅半毫秒 ! 可以說Activiti引擎的開銷非常小。
吞吐量數(shù)字同樣令人印象深刻:
在最好的情況下,將執(zhí)行8741個進(jìn)程。 每秒。 當(dāng)您到達(dá)此處閱讀帖子時,您可能已經(jīng)執(zhí)行了數(shù)百萬個過程 。 您還可以看到這里的4或8個線程之間幾乎沒有什么區(qū)別。 這里大多數(shù)執(zhí)行時間是cpu時間,在這里不會發(fā)生諸如等待數(shù)據(jù)庫鎖定之類的潛在沖突。
在這些數(shù)字中,您還可以輕松地看到Oracle XE不能很好地在多個線程之間進(jìn)行擴(kuò)展(如上所述)。 在以下結(jié)果中,您將看到相同的行為。
流程2:相同,但時間更長(一筆交易)
此過程與上一個過程非常相似。 我們又只有一筆交易。 該過程開始后,我們將進(jìn)行七個無操作通過活動,直到結(jié)束。
這里要注意一些事情:
- 最好的結(jié)果(同樣是4個線程,禁用了歷史記錄)實(shí)際上比以前的簡單過程更好。 但也請注意,單線程執(zhí)行速度要慢一些。 這意味著該過程本身會比較慢,這是合理的,因?yàn)橛懈嗷顒印?但是,在進(jìn)程中使用更多的線程并進(jìn)行更多的活動確實(shí)允許更多潛在的交錯。 在前一種情況下,該線程幾乎沒有誕生,然后再次被殺死。
- 啟用/禁用歷史記錄之間的差異大于以前的過程。 這是合乎邏輯的,因?yàn)檫@里記錄了更多的歷史記錄(對于每個活動,數(shù)據(jù)庫中都有一條記錄)。
- 同樣,Activiti 5.10遠(yuǎn)遠(yuǎn)優(yōu)于Activiti 5.9。
吞吐量數(shù)字遵循以下觀察結(jié)果:在這里有更多的機(jī)會使用線程。 最好的結(jié)果徘徊在每秒12000個進(jìn)程執(zhí)行左右 。 再次,它演示了Activiti引擎的輕量級執(zhí)行。
流程3:一次交易中的并行性
此過程執(zhí)行一個派生的并行網(wǎng)關(guān),而一個并行網(wǎng)關(guān)加入同一事務(wù)。 您會期望獲得與先前結(jié)果類似的結(jié)果,但是您會感到驚訝:
將這些數(shù)字與上一個過程進(jìn)行比較,您會發(fā)現(xiàn)執(zhí)行速度較慢。 那么,即使活動較少,為什么此過程也會變慢? 原因在于并行網(wǎng)關(guān)的實(shí)現(xiàn)方式,尤其是聯(lián)接行為。 在實(shí)現(xiàn)方面,最困難的部分是您需要應(yīng)對多個執(zhí)行到達(dá)聯(lián)接時的情況。 為了確保行為是原子的,我們在內(nèi)部進(jìn)行一些鎖定,并在執(zhí)行樹中獲取所有子執(zhí)行,以查明聯(lián)接是否激活。 因此,與“常規(guī)”活動相比,這是一個“昂貴”的操作。
請注意, 我們在這里只談?wù)?毫秒的單線程和3.59毫秒的MySQL最佳情況 。 給定實(shí)現(xiàn)并行網(wǎng)關(guān)功能所需的功能,如果您要問我的話,這是花生。
吞吐量數(shù)字:
這是第一個實(shí)際上包含一些“邏輯”的過程。 在上述最佳情況下,這意味著可以在一秒鐘內(nèi)執(zhí)行1112個過程。 如果您要問我,真是令人印象深刻! 。
流程4:現(xiàn)在我們到達(dá)某個地方(一筆交易)
在對真實(shí)的業(yè)務(wù)流程進(jìn)行建模時,您已經(jīng)看到了該流程。 但是,由于所有活動都是自動傳遞,因此我們?nèi)栽谝粋€數(shù)據(jù)庫事務(wù)中運(yùn)行它。 在這里,我們還有兩個分叉和兩個聯(lián)接。
看一下最低的數(shù)字:使用一個線程運(yùn)行時,在Oracle上為6.88毫秒 。 考慮到這里發(fā)生的所有事情,這真是太快了。 此處的歷史記錄數(shù)量至少翻了一番(Activiti 5.10),這是有道理的,因?yàn)榇颂幱写罅炕顒訉徍巳罩居涗洝?您還可以看到,這導(dǎo)致此處四個線程的平均時間更長,這可能是由于聯(lián)接的實(shí)現(xiàn)所致。 如果您對Activiti內(nèi)部有一點(diǎn)了解,那么您將了解這意味著執(zhí)行樹中有很多執(zhí)行。 我們有一個較大的并發(fā)根,但也有多個子級,有時它們也是并發(fā)根。
但是,盡管平均時間增加了,但吞吐量無疑會受益:
使用八個線程運(yùn)行此過程,使您可以在一秒鐘內(nèi)執(zhí)行411次此過程。
這里還有一些奇特的地方:Oracle數(shù)據(jù)庫在執(zhí)行更多線程并發(fā)時表現(xiàn)更好。 這與所有其他度量完全相反,在所有其他度量中,Oracle在該環(huán)境中總是較慢(請參見上面的說明)。 我認(rèn)為這與我們在派生/加入時應(yīng)用的內(nèi)部鎖定和強(qiáng)制更新有關(guān),Oracle似乎可以更好地處理它。
流程5:添加一些Java邏輯(單個事務(wù))
我添加了此過程,以了解在過程中添加Java服務(wù)任務(wù)的影響。 在此過程中,第一個活動生成一個隨機(jī)值,將其存儲為過程變量,然后根據(jù)該隨機(jī)值在過程中上升或下降。 上升或下降的機(jī)會約為50/50。
平均時間非常好。 實(shí)際上,結(jié)果與上述過程1和2相同(沒有活動或僅具有自動傳遞)。 這意味著將Java邏輯集成到您的進(jìn)程中的開銷幾乎不存在 (當(dāng)然免費(fèi)是免費(fèi)的)。 當(dāng)然,您仍然可以使用該邏輯編寫慢速代碼,但是您不能為此而怪罪Activiti引擎
吞吐量數(shù)字與過程1和2相當(dāng):非常非常高。 最好的情況是每秒執(zhí)行9000個以上的進(jìn)程 。 這確實(shí)也意味著您自己的Java邏輯有9000次調(diào)用。
P rocess 6,7和8:加入等待狀態(tài)和交易
先前的過程向我們展示了Activiti引擎的全部開銷。 在這里,我們將研究等待狀態(tài)和多個事務(wù)如何影響性能。 為此,我添加了三個包含用戶任務(wù)的測試過程。 對于每個用戶任務(wù),引擎將提交當(dāng)前事務(wù)并將線程返回給客戶端。 由于結(jié)果與這些流程幾乎完全兼容,因此我們將其分組。 這些過程是:
按上述過程的順序,這是平均計(jì)時結(jié)果。 對于第一個過程,僅包含一個用戶任務(wù):
顯然,具有等待狀態(tài)和多個事務(wù)確實(shí)會對性能產(chǎn)生影響。 這也是合乎邏輯的:之前,引擎可以通過不將運(yùn)行時狀態(tài)插入數(shù)據(jù)庫來進(jìn)行優(yōu)化,因?yàn)樵撨^程是在一個事務(wù)中完成的。 現(xiàn)在,整個狀態(tài)(即指向您當(dāng)前位置的指針)需要保存到數(shù)據(jù)庫中。 這樣的過程可能會像這樣“沉睡”很多天,幾個月,幾年……。 Activiti引擎現(xiàn)在不再將其保存在內(nèi)存中,它可以釋放出來以完全關(guān)注其他進(jìn)程。
如果僅用一個用戶任務(wù)檢查過程的結(jié)果,就會發(fā)現(xiàn)在最佳情況下(Oracle,單線程– MySQL上的4個線程非常接近),此操作在6.27毫秒內(nèi)完成。 這確實(shí)非常快,如果考慮到我們在這里進(jìn)行了一些插入(執(zhí)行樹,任務(wù)),一些更新(執(zhí)行樹)和刪除(清理)。
這里的第二個過程包含7個用戶任務(wù):
第二張圖表告訴我們,從邏輯上講,更多事務(wù)意味著更多時間。 最好的情況是,此過程在32.12 ms內(nèi)完成。 這是針對七個事務(wù)的,每個事務(wù)給出4.6毫秒。 因此很明顯,添加等待狀態(tài)時,平均時間以線性方式縮放。 這當(dāng)然是有道理的,因?yàn)榻灰撞皇敲赓M(fèi)的。
另請注意,啟用歷史記錄確實(shí)會在此處增加一些開銷。 這是由于將歷史記錄級別設(shè)置為“審核”,從而將所有用戶任務(wù)信息存儲在歷史記錄表中。 從禁用歷史記錄的Activiti 5.9和啟用歷史記錄的Activiti 5.10之間的區(qū)別也可以注意到這一點(diǎn):這是極少數(shù)情況,其中啟用歷史記錄的Activiti 5.10比禁用歷史記錄的5.9慢。 但是考慮到這里存儲的歷史記錄數(shù)量,這是合乎邏輯的。
第三個過程向我們學(xué)習(xí)了用戶任務(wù)和并行網(wǎng)關(guān)如何交互:
第三張圖告訴我們不多的新知識。 現(xiàn)在,我們有兩個用戶任務(wù),以及更“昂貴”的fork / join(請參見上文)。 平均時間就是我們期望的時間。
吞吐量圖表與您期望的平均時間一致。 每秒70至250個進(jìn)程。 w!
為了節(jié)省空間,您需要單擊它們以將其放大:
流程9:那么范圍呢?
對于最后一個過程,我們將研究“范圍”。 “范圍”是我們內(nèi)部在引擎中的調(diào)用方式,它與可變的可見性,指示進(jìn)程狀態(tài)的指針之間的關(guān)系,事件捕獲等有關(guān)。BPMN2.0在這些范圍內(nèi)有很多情況,例如嵌入式子流程,如此處的流程所示。 基本上,每個子流程都可以具有邊界事件(捕獲錯誤,消息等),這些邊界事件僅在其作用域處于活動狀態(tài)時才應(yīng)用于其內(nèi)部活動。 無需過多討論技術(shù)細(xì)節(jié):要以正確的方式實(shí)現(xiàn)范圍,您需要一些不太瑣碎的邏輯。
這里的示例流程具有4個子流程,彼此嵌套。 內(nèi)部過程使用并發(fā)性,這對于Activiti引擎本身也是一個作用域。 這里還有兩個用戶任務(wù),因此意味著兩個事務(wù)。 因此,讓我們看一下它的性能:
您可以清楚地看到Activiti 5.9和5.10之間的巨大差異。 范圍的確是一個領(lǐng)域,最后,圍繞“流程清理”的修復(fù)程序具有巨大的優(yōu)勢,因?yàn)閯?chuàng)建了許多執(zhí)行對象并將其持久化以表示許多不同的范圍。 在Activiti 5.9上,單線程性能不是很好。 幸運(yùn)的是,從藍(lán)色和紅色條之間的間隙中可以看到,這些作用域確實(shí)允許高并發(fā)性。
Oracle的數(shù)量加上5.10測試的多線程結(jié)果,確實(shí)證明了引擎現(xiàn)在可以有效地處理范圍。 吞吐量圖表證明,該進(jìn)程可以通過更多線程很好地進(jìn)行擴(kuò)展,正如您所看到的,倒數(shù)第二個塊中的紅線和綠線之間存在較大差距。 在最佳情況下,引擎會處理此更為復(fù)雜的過程中的64個過程。
隨機(jī)執(zhí)行
如果您已經(jīng)單擊了帖子開頭的完整報(bào)告,則可能已經(jīng)注意到還針對每種環(huán)境都對隨機(jī)執(zhí)行進(jìn)行了測試。 在此設(shè)置中,完成了2500個流程執(zhí)行,并且兩個流程都是隨機(jī)選擇的。 如這些報(bào)告所示,這意味著超過2500次執(zhí)行,每個進(jìn)程執(zhí)行的次數(shù)幾乎相同(正態(tài)分布)。
最后一張圖表顯示了最佳設(shè)置(Activiti 5.10,禁用了歷史記錄)以及添加更多線程時這些隨機(jī)進(jìn)程執(zhí)行的吞吐量如何:
正如我們在上面的許多測試中所看到的那樣,一旦通過了四個線程,事情就不會改變太多了。 數(shù)字(每秒167個進(jìn)程)證明在實(shí)際情況下(即,多個進(jìn)程同時執(zhí)行),Activiti引擎可以很好地?cái)U(kuò)展。 結(jié)論
平均時序圖清楚地顯示了兩件事:
- Activiti引擎速度快,開銷最小 !
- 啟用或禁用歷史記錄之間的區(qū)別絕對明顯。 有時甚至?xí)p少一半的時間。 所有歷史記錄測試都是使用“審核”級別完成的,但是有一個更簡單的歷史記錄級別(“活動”),可能對用例而言足夠好。 Activiti在歷史記錄配置方面非常靈活,您可以為每個進(jìn)程專門調(diào)整歷史記錄級別。 因此,請考慮一下您的流程需要具備的級別,如果它完全需要?dú)v史記錄的話 !
吞吐量圖表證明,當(dāng)有更多線程可用時(即,任何現(xiàn)代應(yīng)用程序服務(wù)器),引擎可以很好地?cái)U(kuò)展。 Activiti經(jīng)過精心設(shè)計(jì),可用于高吞吐量和可用性(集群)架構(gòu) 。
正如我在引言中所說,數(shù)字就是它們:僅僅是數(shù)字。 我要在這里總結(jié)的主要觀點(diǎn)是Activiti引擎非常輕巧。 使用Activiti自動化業(yè)務(wù)流程的開銷很小。 通常, 如果您需要使業(yè)務(wù)流程或工作流自動化,則希望與任何Java系統(tǒng)進(jìn)行一流的集成,并且您希望所有這些都快速且可擴(kuò)展……所有這些都無需進(jìn)一步!
參考:來自JCG合作伙伴 Joram Barrez 的Activiti Performance Showdown活動,來自“ 小腳走路”博客。
翻譯自: https://www.javacodegeeks.com/2012/07/activiti-performance-showdown.html
總結(jié)
以上是生活随笔為你收集整理的Activiti绩效对决的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓手游充值退款流程(安卓手游充值)
- 下一篇: 代理ip攻击(ip代理防止ddos)