正如最近的一些郵件列表電子郵件和從Google返回的許多信息所表明的那樣,ActiveMQ的SystemUsage尤其是MemoryUsage功能使一些人感到困惑。 我將嘗試解釋有關MemoryUsage的一些細節,這些細節可能有助于理解它的工作方式。 我將不介紹StoreUsage和TempUsage,因為我的同事已經深入 介紹了這些內容 。
您可以使用activemq.xml配置的一部分來指定SystemUsage限制,特別是圍繞代理可以使用的內存,持久性存儲和臨時存儲。 這是ActiveMQ 5.7附帶的默認示例:
<systemUsage><systemUsage><memoryUsage><memoryUsage limit="64 mb"/></memoryUsage><storeUsage><storeUsage limit="100 gb"/></storeUsage><tempUsage><tempUsage limit="50 gb"/></tempUsage></systemUsage>
</systemUsage>
內存使用情況
MemoryUsage似乎引起了最大的混亂,因此我在這里嘗試闡明其內部工作原理。
當消息傳入經紀人時,它必須走到某個地方。 它首先被解組斷絲進入類型的ActiveMQ的命令對象ActiveMQMessage
。 目前,該對象顯然已在內存中,但代理程序并未對其進行跟蹤。
這使我們到達了第一點。
MemoryUsage實際上只是代理所需的字節數計數器,用于跟蹤消息正在使用的JVM內存量。 這為經紀人提供了一些監控和確保我們不會超出極限的方法(稍后會詳細介紹)。 否則,在JVM耗盡堆空間之前,我們可能會在不知道限制的情況下接受消息。
因此,我們從線下傳來了消息。 一旦有了這些信息,代理將查看消息需要路由到哪個目的地(或多個目的地)。 一旦找到目的地,它將“發送”到那里。 目的地將增加消息的引用計數(以稍后知道消息是否被視為“活動”)并繼續對其進行處理。 對于第一個參考計數,內存使用量會增加。 對于最后的引用計數,內存使用量會減少。 如果目標是隊列,它將把消息存儲到一個持久位置,并嘗試將其分發給使用者訂閱。 如果是主題,它將嘗試將其分發給所有訂閱。 一路走來(從最初進入目的地到將消息發送給消費者的訂閱),消息引用計數可以增加或減少。 只要它的引用計數大于或等于1,就會在內存中進行說明。
同樣,MemoryUsage 只是一個對象,它對消息的字節進行計數,以了解已使用了多少JVM內存來保存消息。
所以,現在我們對這些的MemoryUsage是什么樣一個基本的了解,讓我們在一對夫婦的事情仔細看看:
- MemoryUsage層次結構(我可以在策略條目上配置的此目標內存限制是多少?)?
- 生產者流控制
- 在目標和訂閱(生產者和消費者)之間分配內存使用情況?
主代理內存,目標內存,訂閱內存
代理加載后,它將創建自己的SystemUsage對象(或使用配置中指定的對象)。 眾所周知,SystemUsage對象具有與之關聯的MemoryUsage,StoreUsage和TempUsage。 內存組件將稱為代理的主內存。 它是一個使用對象,用于跟蹤總體(目標,預訂等)內存。
創建目的地后,目的地將創建自己的SystemUsage對象(它將創建自己的單獨的Memory,Store和Temp Usage對象),但會將其父對象設置為代理的主SystemUsage對象。 可以單獨調整目標的內存限制(但不能調整“存儲”和“臨時”,它們仍將委派給父級)。 設置目標的內存限制:
<destinationPolicy><policyMap><policyEntries><policyEntry queue=">" memoryLimit="5MB"/></policyEntries></policyMap>
</destinationPolicy>
因此,可以將目標使用情況對象用于更好地控制MemoryUsage,但對于所有使用情況計數,它始終與主內存協調。 此功能可用于限制目標保留的消息數量,以使單個目標無法餓死其他目標。 對于隊列,它也會影響商店光標的高水位線。 隊列具有用于持久消息和非持久消息的不同游標。 如果我們達到高水位線(目標內存限制的閾值),則不會再緩存任何郵件以準備發送,并且可以根據需要將非持久性消息清除到臨時磁盤(如果
StoreCursor將使用FilePendingMessageCursor…否則,它將僅使用VMPendingMessageCursor而不會清除到臨時存儲)。
如果您沒有為單個目標指定內存限制,則目標的SystemUsage將委派給父目標(Main SystemUsage),以獲取所有使用計數。 這意味著它將對所有與內存相關的計數有效地使用代理的Main SystemUsage。
另一方面,消費者訂閱對自己的SystemUsage或MemoryUsage計數器沒有任何概念。 他們將始終使用代理的Main SystemUsage對象。 需要注意的主要事情是,使用FilePendingMessageCursor進行訂閱(例如,對于主題訂閱)時,直到達到光標高水位標記(默認為70%)時,消息才會交換到磁盤上。這意味著將需要達到70%的主內存。 可能要花一會兒時間,并且很多消息都可以保留在內存中。 而且,如果您的訂閱是保存大多數此類消息的訂閱,則交換到磁盤可能需要一段時間。 當主題一次將消息分發到一個訂閱時,如果一個訂閱由于將消息交換到磁盤而停止,則準備接收消息的其余訂閱也會感到速度變慢。
您可以將主題的訂閱的光標水位線設置為低于默認值:
<destinationPolicy><policyMap><policyEntries><policyEntry topic="FOO.BAR.>" cursorMemoryHighWaterMark="30" /></policyEntries></policyMap>
</destinationPolicy>
對于感興趣的人...當消息到達目標時,將在消息上設置MemoryUsage對象,以便當Message.incrementReferenceCount()可以增加內存使用量(第一次引用時)。 因此,這意味著它是由目標的內存使用情況(以及主內存)所引起的,因為目標的內存在使用情況發生變化時也會通知其父級,并且會繼續這樣做。 唯一會改變的是消息是否交換到磁盤上。 交換時,其引用計數將減少,其內存使用量將減少,并且一旦進入磁盤,它將丟失其MemoryUsage對象。 因此,當它恢復活力時,哪個MemoryUsage對象將與該對象相關聯,并且將在哪里對其進行計數? 如果將其交換到隊列的存儲中,則在重組時,它將再次與目標內存使用量相關聯。 如果已將其交換到預訂中的臨時存儲(如FilePendingMessageCursor中的臨時存儲),則在重新構成時,它將不再與目標的內存使用量相關聯。 它將與訂閱的內存使用量(即主內存)相關聯。
生產者流控制
跟蹤消息使用的內存的最大勝利是生產者流控制(PFC) 。 PFC默認情況下處于啟用狀態,當達到使用限制時,基本上會減慢生產者的速度。 這可以防止代理超出其限制并耗盡資源。 對于同步發送的生產者或指定了生產者窗口的異步發送,如果達到系統使用率,則代理將阻止該單個生產者,但不會阻止連接。 取而代之的是它將消息暫時擱置以等待空間可用。 一旦消息被存儲,它將僅發送回ProducerAck。 在此之前,客戶端應該阻止其發送操作(不會阻止連接本身)。 ActiveMQ 5.x客戶端庫可以為您處理此問題。 但是,如果在沒有生產者窗口的情況下發送了異步發送,或者如果生產者的行為不正確并且忽略了ProducerAcks,則PFC實際上會在到達內存時阻塞整個連接。 如果您的使用者共享同一連接,則可能導致死鎖。
如果生產者流控制已關閉,則必須更加注意如何設置系統使用率。 當生產者流控制關閉時,它的基本含義是“經紀人,無論消費者是否能跟上進展,您都必須接受所有傳入的消息”。 這可用于處理到達目的地的傳入消息的峰值。 如果您曾經看到日志中的內存使用嚴重超出了您設置的限制,則可能是PFC已關閉,這是預期的行為。
分割經紀人的主存
所以……我之前說過,目的地的內存使用代理的主內存作為父代,而訂閱沒有自己的內存計數器,它們僅使用代理的主內存。 好吧,這在默認情況下是正確的,但是如果找到原因,則可以進一步調整內存的劃分和限制方式。 這里的想法是,您可以將代理的主內存劃分為“生產者”和“消費者”部分。
生產者部分將用于與進入代理的消息相關的所有事物,因此將在目的地中使用。 因此,這意味著,當一個目標上創建了自己的MemoryUsage,它將使用生產者內存作為其母公司,以及生產者的內存將使用代理的主存儲器的一部分 。
另一方面,消費者部分將用于與向消費者分發消息有關的所有事情。 這意味著訂閱。 與其直接使用代理的主內存進行預訂,不如使用使用方內存(它是主內存的一部分)進行訂閱。 理想情況下,消費者部分和生產者部分將等于整個經紀人的主內存。
要在生產者和使用者之間分配內存,請在主<broker/>
元素上設置splitSystemUsageForProducersConsumers
屬性:
<broker splitSystemUsageForProducersConsumers='true'>
默認情況下,這會將代理的主內存使用量分為生產者60%和消費者40%。 要進一步調整,請在主代理元素上設置producerSystemUsagePortion
和consumerSystemUsagePortion
:
<broker splitSystemUsageForProducersConsumers='true' producerSystemUsagePortion='70' consumerSystemUsagePortion='30'>
你有它。 希望這可以為代理的MemoryUsage帶來一些啟發。
參考: ActiveMQ:在Christian Posta Software博客上,從我們的JCG合作伙伴 Christian Posta 了解內存使用情況 。
翻譯自: https://www.javacodegeeks.com/2012/12/activemq-understanding-memory-usage.html