ActiveMQ學習第六篇:Message-Dispatch(消息發送)的特性

Message Cursors

  ActiveMQ發送持久消息的典型處理方式是:當消息的消費者準備就緒時,消息發送系統把存儲的消息按批次發送給消費者,在發送完一個批次的消息後,指針的標記位置指向下-批次待發送消息的位置,進行後續的發送操作。這是一種比較健壯和靈活的消息發送方式,但大多數情況下,消息的消費者不是一直處於這種理想的活躍狀態。
  因此,從ActiveMQ5. 0. 0版本開始,消息發送系統採用一種混合型的發送模式,當消息消費者處理活躍狀態時,允許消息發送系統直接把持久消息發送給消費者,當消費者處於不活躍狀態下,切換使用Cursors來處理消息的發送。當消息消費者處於活躍狀態並且處理能力比較強時,被持久存儲的消息直接被髮送到與消費者關聯的發送隊列,如下圖在這裏插入圖片描述
  當消息已經出現積壓,消費者再開始活躍;或者消費者的消費速度比消息的發送速度慢時,消息將從Pending Cursor中提取,併發送與消費者關聯的發送隊列。見下圖在這裏插入圖片描述
Store- -based:
  從activemq5.0開始,默認使用此種類型的cursor,其能夠滿足大多數場景的使用要求。同時支持非持久消息的處理,Store-based內 嵌了File-based的模式,非持久消息直接被Non-Persistent Pendi ng Cursor 所處理。工作模式見下圖
在這裏插入圖片描述
VM:
  相關的消息引用存儲在內存中,當滿足條件時,消息直接被髮送到消費者與之相關的發送隊列,處理速度非常快,但出現慢消費者或者消費者長時間處於不活躍狀態的情況下,無法適應。工作模式見下圖:
在這裏插入圖片描述
File-based:
  當內存設置達到設置的限制,消息被存儲到磁盤中的臨時文件中。工作模式見下圖:在這裏插入圖片描述
  在缺省情況下,ActiveMQ會根據使用的Message Store來決定使用何種類型的Message Cursors,但是你可以根據destination來配置Message Cursors。
對Topic subscripbers

<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>

對於queue,有效的類型是storeCursor,vmQueueCursor和fil eQueueCursor

<destinationPolicy>
      <policyMap>
        <policyEntries>
          <policyEntry queue="org.apache.>">
            <deadLetterStrategy>
              <individualDeadLetterStrategy queuePrefix="Test.DLQ."/>
            </deadLetterStrategy>
            <pendingQueuePolicy>
            	<vmQueueCursor />
            </pendingQueuePolicy>
          </policyEntry>
        </policyEntries>
      </policyMap>
 </destinationPolicy>

Asvnc Sends(消息異步發送)

  AciveMQ支持異步和同步發送消息,是可以配置的。通常對於快的消費者,是直接把消息同步發送過去,但對於一個Slow Consumer, 你使用同步發送消息可能出現Producer堵塞等現象,慢消費者適合使用異步發送。

異步發送的配置方式有以下幾種:

  1. ActiveMQ默認設置dispatcheAsync=true是最好的性能設置。如果你處理的是Fast Consumer則使用dispatcheAsync=false。
  2. 在Connection URI級別來配置使用Async Sen
  cf = new ActiveMQConnectionFactory("tcp://locahost:61616?jms.useAsyncSend=true");
  1. 在ConnectionFactory級別來配置使用Async Send
((ActiveMQConnectionFactory)connectionFactory).setUseAsyncSend(true);
  1. 在Connection級別來配置使用Async Send
((ActiveMQConnection)connection).setUseAsyncSend(true);

消息確認:

  ActiveMQ缺省支持批量確認消息,由於批量確認會提高性能。若希望禁止使用經過優化的確認方式,有以下幾種方式:

  1. 在Connection URI 上禁止啓用Optimized Acknowledgements。
cf = new ActiveMQConnectionFactory("tcp://locahost:61616?jms.optimizeAcknowledge=false");
  1. 在ConnectionFactory 上禁止啓用Optimized Acknowledgements。
((ActiveMQConnectionFactory)connectionFactory).setOptimizeAcknowledge(fase);
  1. 在Connection上禁止啓用Optimized Acknowledgements。
((ActiveMQConnection)connection).setOptimizeAcknowledge(true);

Dispatch Policies(分發策略):

  通常ActiveMQ會保證topic consumer 以相同的順序接收來自同一個producer的消息,但有時候也需要保證不同的topic consumer 以相同的順序接收消息,然而,由於多線程和異步處理,不同的topic consumer 可能會以不同的順序接收來自不同producer的消息。在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
使用兩個生產者和一個消費者。可以看到順序是正常的,下面測試兩個生產者使用不同session的多個消費者接受消息。
在這裏插入圖片描述
消費者在這裏插入圖片描述

  public static void main(String[] args) {
        ConnectionFactory cf=new ActiveMQConnectionFactory("tcp://192.168.100.155:61616");
        for(int i =0;i<2;i++){
            Thread t=nw MyT(cf);
            t.start();
        }
    }

在這裏插入圖片描述
這樣就可以看到順序已經亂了。想要按照順序分發需要在xml配置,但是會損失性能

<policyEntry topic="ORDERS.>">
    <dispatchPolicy>
        <strictOrderDispatchPolicy/>
     </dispatchPolicy>
</policyEntry>   

在這裏插入圖片描述
這樣再次測試就可以看到順序了。queue的配置

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

輪訓分發策略:

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

<policyEntry topic="FOO.>">
    <dispatchPolicy>
        <roundRobinDispatchPolicy/>
    </dispatchPolicy>
</policyEntry>   

Optimized Acknowledgetment

  ActiveMQ缺省支持批量確認消息,由於批量確認會提高性能,如果希望在應用程序中禁止經過優化的確認方式,可以採用以下幾種方式:

  1. 在Connection的URI上啓用Optimized Acknowledgements
ActiveMQConnectionFactory  factory = new  ActiveMQConnectionFactory("tcp://localhost:61616?jms.optimizedAcknowledge=true");

  1. 在ActiveMQConnectionFacrory上啓用Optimized Acknowledgements
 factory.setOptimizedAcknowledge(true);

  1. 在Connection上啓用Optimized Acknowledgements
 ActiveMQConnection.setOptimizedAcknowledge();

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

Producer Flow Control(生產者流量控制)

  流量控制的含義:當生產者產生消息過快,超過流量限制的時候,生產者將會被阻塞知道資源可以繼續使用,或者拋出一個JMSException,可以通過來配置。
  同步發送消息的producer會自動使用producer flow control;對於異步發送消息的producer,要使用producer flow control,你先要爲connection配置一個ProducerWindowSize參數,如下:

 ActiveMQConnectionFactory.setProducerWindowSize(1024000);

  ProducerWindowSize是producer在發送消息的過程中,收到broker對於之前發送消息的確認之前,能夠發送消息的最大字節數。
  可以禁用producer flow control,以下是ActiveMQ配置文件的一個例子。

<destinationPolicy>
            <policyMap>
              <policyEntries>
                <policyEntry queue=">" producerFlowControl="false"/>              
        </policyEntries>
            </policyMap>
        </destinationPolicy>

  注意:自從ActiveMQ 5.x中引入新的消息遊標之後,非持久化消息被分流到了臨時文件存儲中,以此來減少非持久化消息傳送使用的內存總量。
  結果就是,你可能會發現一個隊列的內存限制永遠達不到,因爲遊標不需要使用太多的內存。如果你真的想把所有的非持久化消息存放到內存中,並在達到內存限制的時候停掉生產者,你需要配置,示例如下:

<policyEntry queue">" producerFolwControl="true" memoryLimit="10b">
    <pendingQueuePolicy>
        <vmQueueCursor>
    </pendingQueuePolicy>
 </policyEntry >

  上面的配置可以保證所有的非持久化隊列消息都保存在內存中,每一個隊列的內存限制爲10kb.也就是相當於如果發送的消息超過10kb之後,後面的消息就無法發送了。如果存在事務的情況下,比如一次性發送的條數超過限制之後才commit這樣也是會阻塞的。
  配置客戶端異常:爲了應對代理空間不足,而導致的不確定的阻塞send()方法的一種替代方案,就是將其配置的成客戶端拋出的一個異常,通過將sendFailIfNoSpace屬性設置爲true,代理將會引起send()方法失敗,並拋出javax.jms.ResourceAllocationException異常,傳播到客戶端,小面是一個配置例子:

<systemUsage>
            <systemUsage sendFailIfNoSpace="true">
                <memoryUsage>
                    <memoryUsage percentOfJvmHeap="70" />
                </memoryUsage>
                <storeUsage>
                    <storeUsage limit="100 gb"/>
                </storeUsage>
                <tempUsage>
                    <tempUsage limit="50 gb"/>
                </tempUsage>
            </systemUsage>
        </systemUsage>

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

 <systemUsage>
            <systemUsage sendFailIfNoSpaceAfterTimeout="3000">
                <memoryUsage>
                    <memoryUsage percentOfJvmHeap="70" />
                </memoryUsage>
                <storeUsage>
                    <storeUsage limit="100 gb"/>
                </storeUsage>
                <tempUsage>
                    <tempUsage limit="50 gb"/>
                </tempUsage>
            </systemUsage>
        </systemUsage>

可以通過元素的一些屬性來減慢生產者,如下例子:

<systemUsage>
            <systemUsage sendFailIfNoSpace="true">
                <memoryUsage>
                    <memoryUsage limit="64 mb"/>
                </memoryUsage>
                <storeUsage>
                    <storeUsage limit="100 gb"/>
                </storeUsage>
                <tempUsage>
                    <tempUsage limit="50 gb"/>
                </tempUsage>
            </systemUsage>
        </systemUsage>

  可以爲非持久化的消息設置內存限制,爲持久化消息設置磁盤空間,以及爲臨時消息設置總的空間,broker將在減慢生產者之前使用這些空間。具體上述的默認設置,broker將會一直阻塞send()方法的調用,直至一些消息被消費,有了可用的空間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章