在本章中,我們將討論如何調校 Apache ActiveMQ Artemis 以達到最佳效能。
1. 調校持久性
-
為了在使用持久性訊息時從 Apache ActiveMQ Artemis 獲得最佳效能,建議使用檔案儲存。Apache ActiveMQ Artemis 也支援 JDBC 持久性,但是與在本地磁碟上進行持久化相比,持久化到資料庫會產生效能成本。
-
將訊息日誌放在其自己的實體磁碟區上。如果磁碟與其他程序共用,例如事務協調器、資料庫或其他也從其中讀寫的日誌,那麼由於磁碟頭可能會在不同檔案之間跳動,這可能會大大降低效能。唯讀追加日誌的優點之一是磁碟頭的移動最小化 - 如果共用磁碟,則此優點將被破壞。如果您正在使用分頁或大型訊息,請確保將它們也理想地放在單獨的磁碟區上。
-
最少的日誌檔案數量。將
journal-min-files
設定為適合您的平均持續速率的檔案數量。此數字表示日誌檔案池的下限閾值。 -
設定日誌檔案池的上限閾值。(
journal-min-files
是下限閾值)。將journal-pool-files
設定為接近您預期的最大負載的數字。日誌會溢出池,如果需要的話,但會在可能的情況下縮回到上限閾值。這允許重複使用檔案,而不會佔用比需求更多的磁碟空間。如果您看到日誌資料目錄中太頻繁地建立新檔案,即持久化大量資料,則需要增加 journal-pool-size,這樣日誌將會重複使用更多檔案,而不是建立新的資料檔案,從而提高效能。 -
日誌檔案大小。日誌檔案大小應與磁碟上磁柱的容量對齊。預設值 10MiB 在大多數系統上應該足夠。
-
使用
ASYNCIO
日誌。如果使用 Linux,請盡量將日誌類型保持為ASYNCIO
。ASYNCIO
的擴展性會比 Java NIO 更好。 -
調校
journal-buffer-timeout
。可以增加逾時時間以提高吞吐量,但會犧牲延遲。 -
如果您正在運行
ASYNCIO
,則可以通過增加journal-max-io
來獲得更好的效能。如果您正在運行 NIO,請勿更改此參數。 -
如果您 100% 確定不需要斷電耐用性保證,請禁用
journal-data-sync
並使用NIO
或MAPPED
日誌:您將在具有程序故障耐用性保證的情況下,在寫入方面獲得巨大的效能提升。
2. 調校 JMS
如果您正在使用 JMS API,則可以在幾個方面進行一些調整
-
禁用訊息 ID。如果您不需要訊息 ID,請在
MessageProducer
類上使用setDisableMessageID()
方法來禁用訊息 ID。這會減少訊息的大小,並避免建立唯一 ID 的開銷。 -
禁用訊息時間戳記。如果您不需要訊息時間戳記,請在
MessageProducer
類上使用setDisableMessageTimeStamp()
方法來禁用訊息時間戳記。 -
避免使用
ObjectMessage
。ObjectMessage
很方便,但是會帶來代價。ObjectMessage
的主體使用 Java 序列化將其序列化為位元組。即使是很小的物件的 Java 序列化形式也非常冗長,因此會佔用大量線路空間,而且與自定義編組技術相比,Java 序列化速度很慢。僅當您真的無法使用其他訊息類型之一時才使用ObjectMessage
,即如果您在執行階段之前真的不知道有效負載的類型。 -
避免使用
AUTO_ACKNOWLEDGE
。AUTO_ACKNOWLEDGE
模式要求針對客戶端上接收的每個訊息從伺服器發送一個確認,這意味著網路上的流量更多。如果可以,請使用DUPS_OK_ACKNOWLEDGE
或使用CLIENT_ACKNOWLEDGE
或事務性會話,並將多個確認與一個確認/提交捆綁在一起。 -
避免使用持久性訊息。預設情況下,JMS 訊息是持久性的。如果您真的不需要持久性訊息,請將它們設定為非持久性的。持久性訊息在將其持久化到儲存中會產生更多開銷。
-
在單個事務中批量發送或確認多個。Apache ActiveMQ Artemis 僅在提交時才需要網路往返,而不是在每次發送或確認時都執行。
3. 其他調校
在 Apache ActiveMQ Artemis 中還有其他一些地方可以執行一些調校
-
使用非同步發送確認。如果您需要以非事務方式發送持久性訊息,並且需要在對 send() 的呼叫返回時保證它們已到達伺服器,請不要設定將持久性訊息設定為阻塞發送,而是使用非同步發送確認以在單獨的資料流中取回您的發送確認,請參閱 發送和提交的保證 以獲取有關此資訊的更多資訊。
-
使用預先確認模式。在預先確認模式下,訊息會在發送到客戶端
之前
被確認。這減少了線路上的確認流量。有關此資訊的更多資訊,請參閱 額外確認模式。 -
禁用安全性。通過在
broker.xml
中將security-enabled
參數設定為false
,您可以獲得較小的效能提升。 -
禁用持久性。如果您不需要訊息持久性,請通過在
broker.xml
中將persistence-enabled
設定為 false 來完全關閉它。 -
延遲同步事務。在
broker.xml
中將journal-sync-transactional
設定為false
可以在失敗時以損失某些事務的可能性為代價,為您提供更好的事務性持久性效能。有關更多資訊,請參閱 發送和提交的保證。 -
延遲同步非事務。在
broker.xml
中將journal-sync-non-transactional
設定為false
可以在失敗時以損失某些持久性訊息的可能性為代價,為您提供更好的非事務性持久性效能。有關更多資訊,請參閱 發送和提交的保證。 -
非阻塞發送訊息。在 jms 配置(如果您正在使用 JMS 和 JNDI)或直接在 ServerLocator 上將
block-on-durable-send
和block-on-non-durable-send
設定為false
。這表示您不必為每個發送的訊息等待整個網路往返。有關更多資訊,請參閱 發送和提交的保證。 -
如果您有非常快速的消費者,則可以增加 consumer-window-size。這有效地禁用了消費者流量控制。
-
使用核心 API 而不是 JMS。使用 JMS API,您的效能會比使用核心 API 略低,因為所有 JMS 操作都需要先轉換為核心操作,伺服器才能處理它們。如果使用核心 API,請盡量使用將
SimpleString
作為參數的方法。與 java.lang.String 不同,SimpleString
在寫入線路之前不需要複製,因此如果在呼叫之間重複使用SimpleString
實例,則可以避免一些不必要的複製。 -
如果使用 Spring 等框架,請在代理伺服器端永久配置目的地,並在客戶端啟用
cacheDestinations
。有關此資訊的更多資訊,請參閱 設定目的地快取。
4. 調校傳輸設定
-
TCP 緩衝區大小。如果您有快速網路和快速機器,則可以通過增加 TCP 發送和接收緩衝區大小來獲得效能提升。有關此資訊的更多資訊,請參閱 配置傳輸。
請注意,某些作業系統(如較新版本的 Linux)包含 TCP 自動調校,手動設定 TCP 緩衝區大小可能會阻止自動調校工作,實際上會降低效能!
-
增加伺服器上檔案控制代碼的限制。如果您預期伺服器上有大量並行連線,或者客戶端正在快速開啟和關閉連線,則應確保執行伺服器的使用者有權建立足夠的檔案控制代碼。
這在不同的作業系統中有所不同。在 Linux 系統上,您可以在檔案
/etc/security/limits.conf
中增加允許開啟的檔案控制代碼數量,例如新增以下行serveruser soft nofile 20000 serveruser hard nofile 20000
這將允許使用者
serveruser
開啟最多 20000 個檔案控制代碼。 -
使用
batch-delay
並將direct-deliver
設定為 false,以獲得非常小訊息的最佳吞吐量。Apache ActiveMQ Artemis 在broker.xml
中附帶了預先設定的連接器/接收器對(netty-throughput
)和在activemq-jms.xml
中附帶的 JMS 連線工廠(ThroughputConnectionFactory
),可用於提供最佳吞吐量,尤其是對於小型訊息。有關此資訊的更多資訊,請參閱 配置傳輸。
5. 調校虛擬機器 (VM)
我們強烈建議您使用最新的 Java JVM 以獲得最佳效能。我們在內部使用 Sun JVM 進行測試,因此其中一些調校不適用於其他供應商的 JDK(例如 IBM 或 JRockit)
-
記憶體設定。為伺服器提供盡可能多的記憶體。Apache ActiveMQ Artemis 可以通過使用分頁(在 分頁 中描述)在低記憶體中運行,但是如果它可以在 RAM 中運行所有佇列,則這將提高效能。您需要的記憶體量將取決於佇列的大小和數量以及訊息的大小和數量。使用 JVM 引數
-Xms
和-Xmx
設定伺服器可用的 RAM。我們建議將它們設定為相同的高值。在高負載期間,Artemis 很可能會生成和銷毀大量物件。這可能會導致過時物件的累積。為了減少記憶體不足並導致完整 GC(可能會引入暫停和意外行為)的可能性,建議將 JVM 的最大堆大小 (
-Xmx
) 設定為至少為代理伺服器的global-max-size
的 5 倍。例如,在代理伺服器在高負載下運行且global-max-size
為 1GB 的情況下,建議將最大堆大小設定為 5GB。
6. 避免反模式
-
重複使用連線/會話/消費者/生產者。我們看到的最常見的訊息傳遞反模式可能是使用者為他們發送的每個訊息或他們消耗的每個訊息建立新的連線/會話/生產者。這是對資源的拙劣使用。這些物件需要時間來建立,並且可能涉及多次網路往返。始終重複使用它們。
Spring 的
JmsTemplate
以使用這種反模式而聞名。它只能在搭配連線池時安全使用(例如,在 Java EE 應用程式伺服器中使用 JCA),即使如此,也只應僅用於發送訊息。即使使用連線池,它也無法安全地用於同步消費訊息。如果您需要連線池,請參考這裡,它是由 ActiveMQ 的程式碼庫分支而來,成為一個獨立專案,並完全支援 JMS 2。 -
避免過大的訊息。諸如 XML 之類的冗長格式會佔用大量網路空間,並因此導致效能下降。如果可以,請避免在訊息主體中使用 XML。
-
不要為每個請求建立臨時佇列。這種常見的反模式涉及到臨時佇列請求-回應模式。在臨時佇列請求-回應模式中,訊息會被發送到目標,並在 reply-to 標頭中設定本地臨時佇列的位址。當接收者收到訊息時,他們會處理它,然後將回應發送回 reply-to 中指定的位址。使用此模式的一個常見錯誤是在每次發送訊息時都建立一個新的臨時佇列。這會大幅降低效能。相反地,臨時佇列應該重複用於多個請求。
-
不要為了使用而使用 Message-Driven Bean (訊息驅動 Bean)。一旦開始使用 MDB,相較於直接的訊息消費者,每個接收到的訊息的程式碼路徑都會大幅增加,因為會執行許多額外的應用程式伺服器程式碼。請自問您是否真的需要 MDB?您可以使用一般訊息消費者完成相同的任務嗎?
7. 問題排解
7.1. UDP 無法運作
在某些情況下,用於發現的 UDP 可能無法運作。典型的情況包括:
-
節點位於防火牆後方。如果您的節點位於不同的機器上,則防火牆可能會封鎖多播。您可以通過禁用每個節點的防火牆或新增適當的規則來測試這一點。
-
您正在使用家庭網路或位於閘道器後方。通常,家庭網路會將所有 UDP 流量重定向到網際網路服務供應商,然後這些流量會被 ISP 丟棄或直接遺失。要解決此問題,您需要將路由新增到防火牆/閘道器,將任何多播流量重定向回本機網路。
-
所有節點都在同一台機器上。如果是這種情況,則與第 2 點的問題類似,應該可以使用相同的解決方案來解決。或者,您可以將多播路由新增到迴路介面。在 Linux 上,指令會是:
# you should run this as root route add -net 224.0.0.0 netmask 240.0.0.0 dev lo
這會將所有指向 224.0.0.0 的流量重定向到迴路介面。如果根本沒有網路,這也會起作用。在 Mac OS X 上,指令略有不同:
sudo route add 224.0.0.0 127.0.0.1 -netmask 240.0.0.0