ActiveMQ Message Cursors、Async Sends、Optimized Acknowledgement、Producer Flow Control

概述
ActiveMQ發送持久消息的典型處理方式是:當消息的消費者準備就緒時,消息發送系統把存儲的消息按批次發送給消費者,在發送完一個批次的消息後,指針的標記位置指向下一批次待發送消息的位置,進行後續的發送操作。這是一種比較健壯和靈活的消息發送方式,但大多數情況下,消息的消費者不是一直處於這種理想的活躍狀態。
因此,從ActiveMQ5.0.0版本開始,消息發送系統採用一種混合型的發送模式,當消息消費者處理活躍狀態時,允許消息發送系統直接把持久消息發送給消費者,當消費者處於不活躍狀態下,切換使用Cursors來處理消息的發送。

當消息消費者處於活躍狀態並且處理能力比較強時,被持久存儲的消息直接被髮送到與消費者關聯的發送隊列,見下圖


當消息已經出現積壓,消費者再開始活躍;或者消費者的消費速度比消息的發送速度慢時,消息將從Pending Cursor中提取,併發送與消費者關聯的發送隊列。見下圖


Message Cursors分成三種類型
1: Store-based
2:VM

3:File-based

Store-based

從activemq5.0開始,默認使用此種類型的cursor,其能夠滿足大多數場景的使用要求。同時支持非持久消息的處理,Store-based內嵌了File-based的模式,非持久消息直接被Non-Persistent Pending Cursor所處理。工作模式見下圖


VM

相關的消息引用存儲在內存中,當滿足條件時,消息直接被髮送到消費者與之相關的發送隊列,處理速度非常快,但出現慢消費者或者消費者長時間處於不活躍狀態的情況下,無法適應。工作模式見下圖 :


File-based

當內存設置達到設置的限制,消息被存儲到磁盤中的臨時文件中。工作模式見下圖 :


配置使用
在缺省情況下,ActiveMQ會根據使用的Message Store來決定使用何種類型的Message Cursors,但是你可以根據destination來配置Message Cursors,例如:
1:對Topic subscribers
<destinationPolicy>
    <policyMap>
        <policyEntries>
            <policyEntry topic="org.apache.>" producerFlowControl="false" memoryLimit="1mb">
            <dispatchPolicy>
                <strictOrderDispatchPolicy />
            </dispatchPolicy>
            <deadLetterStrategy>
                <individualDeadLetterStrategy topicPrefix="Test.DLQ." />
            </deadLetterStrategy>
            <pendingSubscriberPolicy>
                <vmCursor />
            </pendingSubscriberPolicy>
            <pendingDurableSubscriberPolicy>
                <vmDurableCursor/>
            </pendingDurableSubscriberPolicy>
            </policyEntry>
        </policyEntries>
    </policyMap>

</destinationPolicy>

配置說明:有效的Subscriber類型是vmCursor和fileCursor,缺省是store based cursor。有效的持久化Subscriber的cursor types是storeDurableSubscriberCursor, vmDurableCursor 和fileDurableSubscriberCursor,缺省是store based cursor。
對於Queues的配置
<destinationPolicy>
    <policyMap>
        <policyEntries>
            <policyEntry queue="org.apache.>">
                <deadLetterStrategy>
                    <individualDeadLetterStrategy queuePrefix="Test.DLQ."/>
                </deadLetterStrategy>
                <pendingQueuePolicy>
                    <vmQueueCursor />
                </pendingQueuePolicy>
        </policyEntry>
    </policyEntries>
</policyMap>
</destinationPolicy>

配置說明:有效的類型是storeCursor, vmQueueCursor 和 fileQueueCursor

Async Sends 異步發送
AciveMQ支持異步和同步發送消息,是可以配置的。通常對於快的消費者,是直接把消息同步發送過去,但對於一個Slow Consumer,你使用同步發送消息可能出現Producer堵塞等現象,慢消費者適合使用異步發送。
配置使用
1:ActiveMQ默認設置dispatcheAsync=true是最好的性能設置。如果你處理的是Fast Consumer則使用dispatcheAsync=false
2:在Connection URI級別來配置使用Async Send
cf = new ActiveMQConnectionFactory("tcp://locahost:61616?jms.useAsyncSend=true");
3:在ConnectionFactory級別來配置使用Async Send
((ActiveMQConnectionFactory)connectionFactory).setUseAsyncSend(true);
4:在Connection級別來配置使用Async Send

((ActiveMQConnection)connection).setUseAsyncSend(true);

Dispatch Policies
嚴格順序分發策略(Strict Order Dispatch Policy)
通常ActiveMQ會保證topic consumer以相同的順序接收來自同一個producer的消息,但有時候也需要保證不同的topic consumer以相同的順序接收消息,然而,由於多線程和異步處理,不同的topic consumer可能會以不同的順序接收來自不同producer的消息。
Strict order dispatch policy 會保證每個topic consumer會以相同的順序接收消息,代價是性能上的損失。以下是一個配置例子:
<policyEntry topic="ORDERS.>">
    <dispatchPolicy>
        <strictOrderDispatchPolicy />
    </dispatchPolicy>
</policyEntry>
對於Queue的配置爲:

<policyEntry queue=">" strictOrderDispatch="false" />

輪詢分發策略(Round Robin Dispatch Policy)
ActiveMQ的prefetch缺省參數是針對處理大量消息時的高性能和高吞吐量而設置的,所以缺省的prefetch參數比較大。而且缺省的dispatch policies會嘗試儘可能快的填滿prefetch緩衝。
然而在有些情況下,例如只有少量的消息而且單個消息的處理時間比較長,那麼在缺省的prefetch和dispatch policies下,這些少量的消息總是傾向於被分發到個別的consumer上。這樣就會因爲負載的不均衡分配而導致處理時間的增加。
Round robin dispatch policy會嘗試平均分發消息,以下是一個例子:
<policyEntry topic="ORDERS.>">
    <dispatchPolicy>
        <roundRobinDispatchPolicy/>
    </dispatchPolicy>

</policyEntry>

Optimized Acknowledgement
ActiveMQ缺省支持批量確認消息,由於批量確認會提高性能。如果希望在應用程序中禁止經過優化的確認方式,那麼可以採用如下方法:
1:在Connection URI 上啓用Optimized Acknowledgements
cf = new ActiveMQConnectionFactory("tcp://locahost:61616?jms.optimizeAcknowledge=true");
2:在ConnectionFactory 上啓用Optimized Acknowledgements
((ActiveMQConnectionFactory)connectionFactory).setOptimizeAcknowledge(true);
3:在Connection上啓用Optimized Acknowledgements
((ActiveMQConnection)connection).setOptimizeAcknowledge(true);

4:5.6以後的版本,還可以在Connection URI上設置setOptimizeAcknowledgeTimeOut參數,默認值爲300ms,你可以設置自己要用的值,0表示禁用。

Producer Flow Control

生產者流量控制(Producer Flow Control)
流量控制的含義:當生產者產生消息過快,超過流量限制的時候,生產者將會被阻塞直到資源可以繼續使用,或者拋出一個JMSException,可以通過<systemUsage>來配置。
同步發送消息的producer會自動使用producer flow control ;對於異步發送消息的producer,要使用producer flow control,你先要爲connection配置一個ProducerWindowSize參數,如下:
((ActiveMQConnectionFactory)cf).setProducerWindowSize(1024000);
ProducerWindowSize是producer在發送消息的過程中,收到broker對於之前發送消息的確認之前, 能夠發送消息的最大字節數
可以禁用producer flow control,以下是ActiveMQ配置文件的一個例子
<destinationPolicy>
    <policyMap>
        <policyEntries>
            <policyEntry topic="FOO.>" producerFlowControl="false"/>
        </policyEntries>
    </policyMap>

</destinationPolicy>

注意,自從ActiveMQ 5.x中引入新的消息遊標之後,非持久化消息被分流到了臨時文件存儲中,以此來減少非持久化消息傳送使用的內存總量。
結果就是,你可能會發現一個隊列的內存限制永遠達不到,因爲遊標不需要使用太多的內存。如果你真的想把所有的非持久化消息存放在內存中,並在達到內存限制的時候停掉生產者,你需要配置<vmQueueCursor>,示例如下:
<policyEntry queue=">" producerFlowControl="true" memoryLimit="1mb">
    <pendingQueuePolicy>
        <vmQueueCursor/>
    </pendingQueuePolicy>
</policyEntry>
上面的配置可以保證所有的非持久化隊列消息都保存在內存中,每一個隊列的內存限制爲1Mb配置客戶端的異常

爲了應對代理空間不足,而導致不確定的阻塞send()方法的一種替代方案,就是將其配置成客戶端拋出的一個異常。通過將sendFailIfNoSpace屬性設置爲true,代理將會引起send()方法失敗,並拋出javax.jms.ResourceAllocationException異常,傳播到客戶端。下面是一個配置的示例:

<systemUsage>
    <systemUsage sendFailIfNoSpace="true">
        <memoryUsage>
            <memoryUsage limit="20 mb"/>
        </memoryUsage>
    </systemUsage>
</systemUsage>
這麼配置的好處是,客戶端可以捕獲javax.jms.ResourceAllocationException異常,稍等一下,並重試send()操作,而不是無限期地傻等下去。
從5.3.1版本之後,sendFailIfNoSpaceAfterTimeout屬性被加了進來。這個屬性同樣導致send()方法失敗,並在客戶端拋出異常,但僅當等待了指定時間之後才觸發。如果在配置的等待時間過去之後,代理上的空間仍然沒有被釋放,僅當這個時候send()方法纔會失敗,並且在客戶端拋出異常。示例:
<systemUsage>
    <systemUsage sendFailIfNoSpaceAfterTimeout="3000">
        <memoryUsage>
            <memoryUsage limit="20 mb"/>
        </memoryUsage>
    </systemUsage>
</systemUsage>
定義超時的單位是毫秒

System usage
可以通過<systemUsage>元素的一些屬性來減慢生產者,如下例子:
<systemUsage>
<systemUsage>
<memoryUsage>
<memoryUsage limit="64 mb" />
</memoryUsage>
<storeUsage>
<storeUsage limit="100 gb" />
</storeUsage>
<tempUsage>
<tempUsage limit="10 gb" />
</tempUsage>
</systemUsage>
</systemUsage>
你可以爲非持久化的消息設置內存限制,爲持久化消息設置磁盤空間,以及爲臨時消息設置總的空間,broker將在減慢生產者之前使用這些空間。使用上述的默認設置,broker將會一直阻塞send()方法的調用,直至一些消息被消費,有了可用的空間。

發佈了56 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章