Apache Camel 3.1 –更多骆驼核心优化(第2部分)
我以前曾在博客中介紹我們在下一個Camel 3.1版本(第1部分)中所做的優化 。
今天,我想發布大約4周后的最新狀態更新。
我們集中在三個方面優化駱駝核心:
- 不必要的對象分配
- 不必要的方法調用
- 提高績效
換句話說,我們使Camel創建更少的對象,調用更少的方法并提高路由過程中的性能。
為了幫助識別駱駝核心中的這些問題,我們使用了一條簡單的駱駝路線:
來自timer:foo
記錄:foo
其他時候,我們專注于更長的路線:
來自timer:foo
記錄:foo1
記錄:foo2
記錄:foo3
…
登錄:fooN
或關注bean組件:
來自timer:foo
到bean:foo
等等。 我們還為計時器組件添加了一個不包含元數據的選項,因此消息不包含任何正文,標頭或交換屬性。 這使我們可以專注于純路由引擎及其開銷。
因此,所有這些共同幫助確定了許多較小的改進點,共同取得了巨大的成功。
tl:dr –顯示數字
好吧,讓我們先發布一些數字,然后再詳細說明已完成的工作。
對象分配–(5分鐘采樣)
駱駝2.25 2.9 M創建對象
駱駝3.0 55 M對象創建
駱駝3.1 1.8 M對象創建
好的,我們必須承認Camel 3.0在路由過程中存在過多的對象分配問題。 沒有內存泄漏,但是會創建很多不必要的對象。 我將在下面詳細說明原因。
但是,有趣的是駱駝2.25和3.1之間的增益(創建的對象減少40%)。
方法調用–(5分鐘采樣)
駱駝2.25 139種不同的駱駝使用方法
駱駝3.0 167種不同的駱駝使用方法
Camel 3.1使用84種不同的Camel方法
上表列出了Camel在路由過程中從Camel調用的方法數量。 數據不包括JDK中的所有方法。 由于我們無法優化它們,但是我們可以優化Camel源代碼。
從表中可以看出,我們已有改進。 駱駝3.1的使用率不到3.0的一半,比駱駝2.2.5的使用率低40%。
駱駝3.0
好的,所以Camel 3.0在使用過多內存方面存在問題。 一個重要的原因是新的反應式執行器現在可以通過事件循環執行路由中的每個步驟,方法是將任務移交給隊列,并讓工作人員執行任務。 因此,此切換現在需要創建其他對象并將任務存儲在隊列中等。
最大的一些成功是避免創建TRACE日志消息,不幸的是,無論是否啟用了TRACE日志記錄級別,始終都會創建該消息。 另一個大勝利是避免使用子元素創建路由過程的toString表示形式。 取而代之的是,駱駝現在只輸出進程的id,這是一個快速的操作,并且不分配新對象。
另一個問題是使用java.util.stream的新代碼。 這既是祝福也是詛咒(主要是對快速代碼的詛咒)。 因此,通過使用普通的for循環,if結構并在核心路由引擎的關鍵部分避免使用java.util.stream,我們減少了對象分配。
Camel 3也是高度模塊化的,例如,在Camel 2.x中,我們將所有類都放在同一類路徑中,并且可以使用instanceof檢查。 因此,在Camel 3中,我們有一些代碼在執行此類檢查時表現很差(再次是Java util流)。
另一個問題是被動執行器,它使用LinkedList作為隊列。 因此,如果您有任務進入隊列并且工作人員以相同的速度處理它們,因此隊列為空/耗盡,那么LinkedList的性能會很差,因為它會不斷分配/釋放對象。 通過切換到預分配大小為16的ArrayQueue,則隊列中始終有空間容納任務,并且不會發生分配/取消分配。
還有更多優化,但是上面提到的優化可能是最大的問題。 然后,許多較小的優化組合在一起。
許多較小的優化
駱駝的UUID生成器正在使用一些字符串連接,這會花費很多。 我們減少了在消息和工作單元中生成UUID的需求,因此每個交換只生成1個。
駱駝路由引擎中的內部建議(建議= AOP之前/之后)。 這些建議中的一些具有從前到后都需要保留的狀態,這意味著需要存儲對象。 在我們為所有建議分配一個數組之前,即使對于那些沒有狀態的建議也是如此,因此存儲了null。 現在,我們僅分配具有狀態的建議的確切數目的數組。 (非常小的勝利,例如object [6] vs object [2]等,但這是在駱駝路線中的每一步發生的,所以總的來說是合計的。) 另一個勝利是,如果不需要內部路由處理器,則避免在UnitOfWork周圍進行AOP。 這避免了額外的方法調用,并為after任務分配了一個回調對象。 由于所有這些都發生在路由的每個步驟中,因此是一個很好的改進。
一些最常用的EIP已經過優化。 例如
允許您使用其他MEP將消息發送到端點(但是很少使用)。 現在,EIP會檢測到此情況,并避免創建用于恢復MEP的回調對象。 管道EIP(例如,當您執行->到-> to時)也對使用索引計數器而不是java.util.Iterator進行了一些改進,因為后者分配了一個額外的對象
駱駝還有一個秒表,它使用java.util.Date來存儲時間。 已對其進行優化以使用長值。
另一個改進是事件通知。 現在,我們會預先計算其是否正在使用中,并避免在與路由消息相關的事件中一起調用它們。 順便說一句,在Camel 3.0中,事件通知程序被重構為使用Java 8 Supplier的API和許多精美的API,但是所有這些都會產生大量開銷。 在Camel 3.1中,我們已將通知程序恢復為與以前在Camel 2.x中一樣,并進行了其他優化。
因此,讓我以說……結束這個博客。 太棒了 Camel 3.1將使用更少的內存,通過不調用太多方法來執行得更快(請記住,我們可能不得不移動一些需要調用的代碼,但是以不同的方式執行此操作,以避免調用太多的方法)。
在涉及的源代碼方面,最大的變化之一是從使用ServiceSupport(Camel中很多東西的基類)中的基于實例的記錄器切換為使用靜態記錄器實例。 這意味著將創建更少的Logger對象,這也是一種更好的做法。
更好的性能
其他改進是,我們將駱駝作為交換屬性保留的某些內部狀態直接移到了Exchange的字段中。 這樣可以避免在屬性映射中存儲鍵/值,但是我們可以使用諸如boolean,int等原語。由于通過getter獲取布爾值要比通過鍵在Map中查找值更快,因此這樣做的效果也更好。
實際上,在Camel 3.1中,然后在常規路由期間,Camel不會從交換屬性中查找任何此類狀態,這意味著沒有方法調用。 仍然有一些狀態存儲為交換屬性(將來可能會改善其中一些狀態,但是這些狀態中的大多數僅很少使用)。 我們優化的是在路由過程中始終檢查和使用的狀態。
交換getProperty(5分鐘采樣)
駱駝2.25 572598 getPropety(字符串)
駱駝2.25 161502 getPropety(字符串,對象)
駱駝2.25 161502 getPropety(字符串,對象,類)
駱駝2.25 141962 getPropeties()
駱駝3.0 574944 getProperty(字符串)
Camel 3.0 167904 getPropety(字符串,對象)
駱駝3.0 167904 getPropety(字符串,對象,類)
駱駝3.0 91584 getPropeties()
駱駝3.1 0 getProperty(String)
駱駝3.1 0 getPropety(String,Object)
駱駝3.1 0 getPropety(String,Object,Class)
駱駝3.1 0 getPropeties()
如您所見,Camel 2.25和3.0查找了很多這種狀態。 在Camel 3.1中,我們對此進行了極大的優化,并且根本沒有查找-就像說狀態以原始類型存儲在Exchange上一樣,JDK可以內聯并執行得非常快。
以下屏幕截圖顯示了駱駝2.25和3.1。 (3.1的屏幕截圖與昨天相比略有過時,此后我們對Camel進行了優化)。 請參見下面的屏幕截圖:
好的,還有許多其他較小的優化,在撰寫此博客時,我目前正在研究一個優化。 好的,讓我結束這個博客,并保存第3部分的詳細信息。
翻譯自: https://www.javacodegeeks.com/2020/02/apache-camel-3-1-more-camel-core-optimizations-coming-part-2.html
總結
以上是生活随笔為你收集整理的Apache Camel 3.1 –更多骆驼核心优化(第2部分)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java lambda函数_Java S
- 下一篇: 润滑油的主要成分 润滑油的主要成分有哪些