ActiveMQ消息傳送機制以及ACK機制詳解

AcitveMQ是做爲一種消息存儲和分發組件,涉及到client與broker端數據交互的方方面面,它不只要擔保消息的存儲安全性,還要提供額外的手段來確保消息的分發是可靠的。java

一. ActiveMQ消息傳送機制緩存

    Producer客戶端使用來發送消息的, Consumer客戶端用來消費消息;它們的協同中心就是ActiveMQ broker,broker也是讓producer和consumer調用過程解耦的工具,最終實現了異步RPC/數據交換的功能。隨着ActiveMQ的不斷髮展,支持了愈來愈多的特性,也解決開發者在各類場景下使用ActiveMQ的需求。好比producer支持異步調用;使用flow control機制讓broker協同consumer的消費速率;consumer端可使用prefetchACK來最大化消息消費的速率;提供"重發策略"等來提升消息的安全性等。在此咱們不詳細介紹。安全

    一條消息的生命週期以下:網絡


  

     圖片中簡單的描述了一條消息的生命週期,不過在不一樣的架構環境中,message的流動行可能更加複雜.將在稍後有關broker的架構中詳解..一條消息從producer端發出以後,一旦被broker正確保存,那麼它將會被consumer消費,而後ACK,broker端纔會刪除;不過當消息過時或者存儲設備溢出時,也會終結它。session


 

     這是一張很複雜,並且有些凌亂的圖片;這張圖片中簡單的描述了:1)producer端如何發送消息 2) consumer端如何消費消息 3) broker端如何調度。若是用文字來描述圖示中的概念,恐怕一言難盡。圖示中,說起到prefetchAck,以及消息同步、異步發送的基本邏輯;這對你瞭解下文中的ACK機制將有很大的幫助。架構

二. optimizeACK負載均衡

    "可優化的ACK",這是ActiveMQ對於consumer在消息消費時,對消息ACK的優化選項,也是consumer端最重要的優化參數之一,你能夠經過以下方式開啓:異步

    1) 在brokerUrl中增長以下查詢字符串: tcp

Java代碼 工具

  1. String brokerUrl = "tcp://localhost:61616?" +   
  2.                    "jms.optimizeAcknowledge=true" +   
  3.                    "&jms.optimizeAcknowledgeTimeOut=30000" +   
  4.                    "&jms.redeliveryPolicy.maximumRedeliveries=6";  
  5. ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(brokerUrl);  

    2) 在destinationUri中,增長以下查詢字符串:

Java代碼 

  1. String queueName = "test-queue?customer.prefetchSize=100";  
  2. Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);  
  3. Destination queue = session.createQueue(queueName);  

    咱們須要在brokerUrl指定optimizeACK選項,在destinationUri中指定prefetchSize(預獲取)選項,其中brokerUrl參數選項是全局的,即當前factory下全部的connection/session/consumer都會默認使用這些值;而destinationUri中的選項,只會在使用此destination的consumer實例中有效;若是同時指定,brokerUrl中的參數選項值將會被覆蓋。optimizeAck表示是否開啓「優化ACK」,只有在爲true的狀況下,prefetchSize(下文中將會簡寫成prefetch)以及optimizeAcknowledgeTimeout參數纔會有意義。此處須要注意"optimizeAcknowledgeTimeout"選項只能在brokerUrl中配置。

    prefetch值建議在destinationUri中指定,由於在brokerUrl中指定比較繁瑣;在brokerUrl中,queuePrefetchSize和topicPrefetchSize都須要單獨設定:"&jms.prefetchPolicy.queuePrefetch=12&jms.prefetchPolicy.topicPrefetch=12"等來逐個指定。

    若是prefetchACK爲true,那麼prefetch必須大於0;當prefetchACK爲false時,你能夠指定prefetch爲0以及任意大小的正數。不過,當prefetch=0是,表示consumer將使用PULL(拉取)的方式從broker端獲取消息,broker端將不會主動push消息給client端,直到client端發送PullCommand時;當prefetch>0時,就開啓了broker push模式,此後只要當client端消費且ACK了必定的消息以後,會當即push給client端多條消息。

    當consumer端使用receive()方法同步獲取消息時,prefetch能夠爲0和任意正值;當prefetch=0時,那麼receive()方法將會首先發送一個PULL指令並阻塞,直到broker端返回消息爲止,這也意味着消息只能逐個獲取(相似於Request<->Response),這也是Activemq中PULL消息模式;當prefetch > 0時,broker端將會批量push給client 必定數量的消息(<= prefetch),client端會把這些消息(unconsumedMessage)放入到本地的隊列中,只要此隊列有消息,那麼receive方法將會當即返回,當必定量的消息ACK以後,broker端會繼續批量push消息給client端。

    當consumer端使用MessageListener異步獲取消息時,這就須要開發設定的prefetch值必須 >=1,即至少爲1;在異步消費消息模式中,設定prefetch=0,是相悖的,也將得到一個Exception。

    此外,咱們還能夠brokerUrl中配置「redelivery」策略,好比當一條消息處理異常時,broker端能夠重發的最大次數;和下文中提到REDELIVERED_ACK_TYPE互相協同。當消息須要broker端重發時,consumer會首先在本地的「deliveredMessage隊列」(Consumer已經接收但還未確認的消息隊列)刪除它,而後向broker發送「REDELIVERED_ACK_TYPE」類型的確認指令,broker將會把指令中指定的消息從新添加到pendingQueue(亟待發送給consumer的消息隊列)中,直到合適的時機,再次push給client。

    到目前爲止,或許你知道了optimizeACK和prefeth的大概意義,不過咱們可能還會有些疑惑!!optimizeACK和prefetch配合,將會達成一個高效的消息消費模型:批量獲取消息,並「延遲」確認(ACK)prefetch表達了「批量獲取」消息的語義,broker端主動的批量push多條消息給client端,總比client屢次發送PULL指令而後broker返回一條消息的方式要優秀不少,它不只減小了client端在獲取消息時阻塞的次數和阻塞的時間,還可以大大的減小網絡開支。optimizeACK表達了「延遲確認」的語義(ACK時機),client端在消費消息後暫且不發送ACK,而是把它緩存下來(pendingACK),等到這些消息的條數達到必定閥值時,只須要經過一個ACK指令把它們所有確認;這比對每條消息都逐個確認,在性能上要提升不少。因而可知,prefetch優化了消息傳送的性能,optimizeACK優化了消息確認的性能。

    當consumer端消息消費的速率很高(相對於producer生產消息),並且消息的數量也很大時(好比消息源源不斷的生產),咱們使用optimizeACK + prefetch將會極大的提高consumer的性能。不過反過來:

    1) 若是consumer端消費速度很慢(對消息的處理是耗時的),過大的prefetchSize,並不能有效的提高性能,反而不利於consumer端的負載均衡(只針對queue);按照良好的設計準則,當consumer消費速度很慢時,咱們一般會部署多個consumer客戶端,並使用較小的prefetch,同時關閉optimizeACK,可讓消息在多個consumer間「負載均衡」(即均勻的發送給每一個consumer);若是較大的prefetchSize,將會致使broker一次性push給client大量的消息,可是這些消息須要好久才能ACK(消息積壓),並且在client故障時,還會致使這些消息的重發。

    2) 若是consumer端消費速度很快,可是producer端生成消息的速率較慢,好比生產者10秒鐘生成10條消息,可是consumer一秒就能消費完畢,並且咱們還部署了多個consumer!!這種場景下,建議開啓optimizeACK,可是須要設置的prefetchSize不能過大;這樣能夠保證每一個consumer都能有"活幹",不然將會出現一個consumer很是忙碌,可是其餘consumer幾乎收不到消息。

    3) 若是消息很重要,特別是不肯意接收到」redelivery「的消息,那麼咱們須要將optimizeACK=false,prefetchSize=1

    既然optimizeACK是」延遲「確認,那麼就引入一種潛在的風險:在消息被消費以後尚未來得及確認時,client端發生故障,那麼這些消息就有可能會被從新發送給其餘consumer,那麼這種風險就須要client端可以容忍「重複」消息。

    prefetch值默認爲1000,固然這個值可能在不少場景下是偏大的;咱們暫且不考慮ACK模式(參見下文),一般狀況下,咱們只須要簡單的統計出單個consumer每秒的最大消費消息數便可,好比一個consumer每秒能夠處理100個消息,咱們指望consumer端每2秒確認一次,那麼咱們的prefetchSize能夠設置爲100 * 2 /0.65大概爲300。不管如何設定此值,client持有的消息條數最大爲:prefetch + 「DELIVERED_ACK_TYPE消息條數」(DELIVERED_ACK_TYPE參見下文)

**     即便當optimizeACK爲true,也只會當session的ACK模式爲AUTO_ACKNOWLEDGE時纔會生效**,即在其餘類型的ACK模式時consumer端仍然不會「延遲確認」,即:

Java代碼 

  1. consumer.optimizeAck = connection.optimizeACK && session.isAutoAcknowledge()  

    當consumer.optimizeACK有效時,若是客戶端已經消費但還沒有確認的消息(deliveredMessage)達到prefetch * 0.65,consumer端將會自動進行ACK;同時若是離上一次ACK的時間間隔,已經超過"optimizeAcknowledgeTimout"毫秒,也會致使自動進行ACK。

    此外簡單的補充一下,批量確認消息時,只須要在ACK指令中指明「firstMessageId」和「lastMessageId」便可,即消息區間,那麼broker端就知道此consumer(根據consumerId識別)須要確認哪些消息。

 
三. ACK模式與類型介紹

    JMS API中約定了Client端可使用四種ACK模式,在javax.jms.Session接口中:

  • AUTO_ACKNOWLEDGE = 1    自動確認
  • CLIENT_ACKNOWLEDGE = 2    客戶端手動確認   
  • DUPS_OK_ACKNOWLEDGE = 3    自動批量確認
  • SESSION_TRANSACTED = 0    事務提交併確認

    此外AcitveMQ補充了一個自定義的ACK模式:

  • INDIVIDUAL_ACKNOWLEDGE = 4    單條消息確認

    咱們在開發JMS應用程序的時候,會常用到上述ACK模式,其中"INDIVIDUAL_ACKNOWLEDGE "只有ActiveMQ支持,固然開發者也可使用它. ACK模式描述了Consumer與broker確認消息的方式(時機),好比當消息被Consumer接收以後,Consumer將在什麼時候確認消息。對於broker而言,只有接收到ACK指令,纔會認爲消息被正確的接收或者處理成功了,經過ACK,能夠在consumer(/producer)與Broker之間創建一種簡單的「擔保」機制. 

    Client端指定了ACK模式,可是在Client與broker在交換ACK指令的時候,還須要告知ACK_TYPE,ACK_TYPE表示此確認指令的類型,不一樣的ACK_TYPE將傳遞着消息的狀態,broker能夠根據不一樣的ACK_TYPE對消息進行不一樣的操做。

    好比Consumer消費消息時出現異常,就須要向broker發送ACK指令,ACK_TYPE爲"REDELIVERED_ACK_TYPE",那麼broker就會從新發送此消息。在JMS API中並無定義ACT_TYPE,由於它一般是一種內部機制,並不會面向開發者。ActiveMQ中定義了以下幾種ACK_TYPE(參看MessageAck類):

  • DELIVERED_ACK_TYPE = 0    消息"已接收",但還沒有處理結束
  • STANDARD_ACK_TYPE = 2    "標準"類型,一般表示爲消息"處理成功",broker端能夠刪除消息了
  • POSION_ACK_TYPE = 1    消息"錯誤",一般表示"拋棄"此消息,好比消息重發屢次後,都沒法正確處理時,消息將會被刪除或者DLQ(死信隊列)
  • REDELIVERED_ACK_TYPE = 3    消息需"重發",好比consumer處理消息時拋出了異常,broker稍後會從新發送此消息
  • INDIVIDUAL_ACK_TYPE = 4    表示只確認"單條消息",不管在任何ACK_MODE下    
  • UNMATCHED_ACK_TYPE = 5    在Topic中,若是一條消息在轉發給「訂閱者」時,發現此消息不符合Selector過濾條件,那麼此消息將 不會轉發給訂閱者,消息將會被存儲引擎刪除(至關於在Broker上確認了消息)。

    到目前爲止,咱們已經清楚了大概的原理: Client端在不一樣的ACK模式時,將意味着在不一樣的時機發送ACK指令,每一個ACK Command中會包含ACK_TYPE,那麼broker端就能夠根據ACK_TYPE來決定此消息的後續操做. 接下來,咱們詳細的分析ACK模式與ACK_TYPE.

Java代碼 

  1. Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);   

    咱們須要在建立Session時指定ACK模式,因而可知,ACK模式將是session共享的,意味着一個session下全部的 consumer都使用同一種ACK模式。在建立Session時,開發者不能指定除ACK模式列表以外的其餘值.若是此session爲事務類型,用戶指定的ACK模式將被忽略,而強制使用"SESSION_TRANSACTED"類型;若是session非事務類型時,也將不能將 ACK模式設定爲"SESSION_TRANSACTED",畢竟這是相悖的.   


 

    Consumer消費消息的風格有2種: 同步/異步..使用consumer.receive()就是同步,使用messageListener就是異步;在同一個consumer中,咱們不能同時使用這2種風格,好比在使用listener的狀況下,當調用receive()方法將會得到一個Exception。兩種風格下,消息確認時機有所不一樣。

    "同步"僞代碼:

Java代碼 

  1. //receive僞代碼---過程  
  2. Message message = sessionMessageQueue.dequeue();  
  3. if(message != null){  
  4.     ack(message);  
  5. }  
  6. return message  

    同步調用時,在消息從receive方法返回以前,就已經調用了ACK;所以若是Client端沒有處理成功,此消息將丟失(可能重發,與ACK模式有關)。

    "異步"僞代碼:

Java代碼 

  1. //基於listener  
  2. Session session = connection.getSession(consumerId);  
  3. sessionQueueBuffer.enqueue(message);  
  4. Runnable runnable = new Ruannale(){  
  5.     run(){  
  6.         Consumer consumer = session.getConsumer(consumerId);  
  7.         Message md = sessionQueueBuffer.dequeue();  
  8.         try{  
  9.             consumer.messageListener.onMessage(md);  
  10.             ack(md);//  
  11.         }catch(Exception e){  
  12.             redelivery();//sometime,not all the time;  
  13.     }  
  14. }  
  15. //session中將採起線程池的方式,分發異步消息  
  16. //所以同一個session中多個consumer能夠並行消費  
  17. threadPool.execute(runnable);  

    基於異步調用時,消息的確認是在onMessage方法返回以後,若是onMessage方法異常,會致使消息不能被ACK,會觸發重發。

四. ACK模式詳解

    AUTO_ACKNOWLEDGE : 自動確認,這就意味着消息的確認時機將有consumer擇機確認."擇機確認"彷佛充滿了不肯定性,這也意味着,開發者必須明確知道"擇機確認"的具體時機,不然將有可能致使消息的丟失,或者消息的重複接收.那麼在ActiveMQ中,AUTO_ACKNOWLEDGE是如何運做的呢?

    1) 對於consumer而言,optimizeAcknowledge屬性只會在AUTO_ACK模式下有效。

    2) 其中DUPS_ACKNOWLEGE也是一種潛在的AUTO_ACK,只是確認消息的條數和時間上有所不一樣。

    3) 在「同步」(receive)方法返回message以前,會檢測optimizeACK選項是否開啓,若是沒有開啓,此單條消息將當即確認,因此在這種狀況下,message返回以後,若是開發者在處理message過程當中出現異常,會致使此消息也不會redelivery,即"潛在的消息丟失";若是開啓了optimizeACK,則會在unAck數量達到prefetch * 0.65時確認,固然咱們能夠指定prefetchSize = 1來實現逐條消息確認。

    4) 在"異步"(messageListener)方式中,將會首先調用listener.onMessage(message),此後再ACK,若是onMessage方法異常,將致使client端補充發送一個ACK_TYPE爲REDELIVERED_ACK_TYPE確認指令;若是onMessage方法正常,消息將會正常確認(STANDARD_ACK_TYPE)。此外須要注意,消息的重發次數是有限制的,每條消息中都會包含「redeliveryCounter」計數器,用來表示此消息已經被重發的次數,若是重發次數達到閥值,將會致使發送一個ACK_TYPE爲POSION_ACK_TYPE確認指令,這就致使broker端認爲此消息沒法消費,此消息將會被刪除或者遷移到"dead letter"通道中。

    所以當咱們使用messageListener方式消費消息時,一般建議在onMessage方法中使用try-catch,這樣能夠在處理消息出錯時記錄一些信息,而不是讓consumer不斷去重發消息;若是你沒有使用try-catch,就有可能會由於異常而致使消息重複接收的問題,須要注意你的onMessage方法中邏輯是否可以兼容對重複消息的判斷。


 
 
 
 

    CLIENT_ACKNOWLEDGE : 客戶端手動確認,這就意味着AcitveMQ將不會「自做主張」的爲你ACK任何消息,開發者須要本身擇機確認。在此模式下,開發者須要須要關注幾個方法:1) message.acknowledge(),2) ActiveMQMessageConsumer.acknowledege(),3) ActiveMQSession.acknowledge();其1)和3)是等效的,將當前session中全部consumer中還沒有ACK的消息都一塊兒確認,2)只會對當前consumer中那些還沒有確認的消息進行確認。開發者能夠在合適的時機必須調用一次上述方法。爲了不混亂,對於這種ACK模式下,建議一個session下只有一個consumer。

    咱們一般會在基於Group(消息分組)狀況下會使用CLIENT_ACKNOWLEDGE,咱們將在一個group的消息序列接受完畢以後確認消息(組);不過當你認爲消息很重要,只有當消息被正確處理以後才能確認時,也可使用此模式  。

    若是開發者忘記調用acknowledge方法,將會致使當consumer重啓後,會接受到重複消息,由於對於broker而言,那些還沒有真正ACK的消息被視爲「未消費」。

    開發者能夠在當前消息處理成功以後,當即調用message.acknowledge()方法來"逐個"確認消息,這樣能夠儘量的減小因網絡故障而致使消息重發的個數;固然也能夠處理多條消息以後,間歇性的調用acknowledge方法來一次確認多條消息,減小ack的次數來提高consumer的效率,不過這仍然是一個利弊權衡的問題。

    除了message.acknowledge()方法以外,ActiveMQMessageConumser.acknowledge()和ActiveMQSession.acknowledge()也能夠確認消息,只不過前者只會確認當前consumer中的消息。其中sesson.acknowledge()和message.acknowledge()是等效的。

    不管是「同步」/「異步」,ActiveMQ都不會發送STANDARD_ACK_TYPE,直到message.acknowledge()調用。若是在client端未確認的消息個數達到prefetchSize * 0.5時,會補充發送一個ACK_TYPE爲DELIVERED_ACK_TYPE的確認指令,這會觸發broker端能夠繼續push消息到client端。(參看PrefetchSubscription.acknwoledge方法)

    在broker端,針對每一個Consumer,都會保存一個由於"DELIVERED_ACK_TYPE"而「拖延」的消息個數,這個參數爲prefetchExtension,事實上這個值不會大於prefetchSize * 0.5,由於Consumer端會嚴格控制DELIVERED_ACK_TYPE指令發送的時機(參見ActiveMQMessageConsumer.ackLater方法),broker端經過「prefetchExtension」與prefetchSize互相配合,來決定即將push給client端的消息個數,count = prefetchExtension + prefetchSize - dispatched.size(),其中dispatched表示已經發送給client端可是尚未「STANDARD_ACK_TYPE」的消息總量;因而可知,在CLIENT_ACK模式下,足夠快速的調用acknowledge()方法是決定consumer端消費消息的速率;若是client端由於某種緣由致使acknowledge方法未被執行,將致使大量消息不能被確認,broker端將不會push消息,事實上client端將處於「假死」狀態,而沒法繼續消費消息。咱們要求client端在消費1.5*prefetchSize個消息以前,必須acknowledge()一次;一般咱們老是每消費一個消息調用一次,這是一種良好的設計。

    此外須要額外的補充一下:全部ACK指令都是依次發送給broker端,在CLIET_ACK模式下,消息在交付給listener以前,都會首先建立一個DELIVERED_ACK_TYPE的ACK指令,直到client端未確認的消息達到"prefetchSize * 0.5"時纔會發送此ACK指令,若是在此以前,開發者調用了acknowledge()方法,會致使消息直接被確認(STANDARD_ACK_TYPE)。broker端一般會認爲「DELIVERED_ACK_TYPE」確認指令是一種「slow consumer」信號,若是consumer不能及時的對消息進行acknowledge而致使broker端阻塞,那麼此consumer將會被標記爲「slow」,此後queue中的消息將會轉發給其餘Consumer。

    DUPS_OK_ACKNOWLEDGE : "消息可重複"確認,意思是此模式下,可能會出現重複消息,並非一條消息須要發送屢次ACK才行。它是一種潛在的"AUTO_ACK"確認機制,爲批量確認而生,並且具備「延遲」確認的特色。對於開發者而言,這種模式下的代碼結構和AUTO_ACKNOWLEDGE同樣,不須要像CLIENT_ACKNOWLEDGE那樣調用acknowledge()方法來確認消息。

    1) 在ActiveMQ中,若是在Destination是Queue通道,咱們真的能夠認爲DUPS_OK_ACK就是「AUTO_ACK + optimizeACK + (prefetch > 0)」這種狀況,在確認時機上幾乎徹底一致;此外在此模式下,若是prefetchSize =1 或者沒有開啓optimizeACK,也會致使消息逐條確認,從而失去批量確認的特性。

    2) 若是Destination爲Topic,DUPS_OK_ACKNOWLEDGE纔會產生JMS規範中詮釋的意義,即不管optimizeACK是否開啓,都會在消費的消息個數>=prefetch * 0.5時,批量確認(STANDARD_ACK_TYPE),在此過程當中,不會發送DELIVERED_ACK_TYPE的確認指令,這是1)和AUTO_ACK的最大的區別。

    這也意味着,當consumer故障重啓後,那些還沒有ACK的消息會從新發送過來。

    SESSION_TRANSACTED : 當session使用事務時,就是使用此模式。在事務開啓以後,和session.commit()以前,全部消費的消息,要麼所有正常確認,要麼所有redelivery。這種嚴謹性,一般在基於GROUP(消息分組)或者其餘場景下特別適合。在SESSION_TRANSACTED模式下,optimizeACK並不能發揮任何效果,由於在此模式下,optimizeACK會被強制設定爲false,不過prefetch仍然能夠決定DELIVERED_ACK_TYPE的發送時機。

    由於Session非線程安全,那麼當前session下全部的consumer都會共享同一個transactionContext;同時建議,一個事務類型的Session中只有一個Consumer,以免rollback()或者commit()方法被多個consumer調用而形成的消息混亂。

    當consumer接受到消息以後,首先檢測TransactionContext是否已經開啓,若是沒有,就會開啓並生成新的transactionId,並把信息發送給broker;此後將檢測事務中已經消費的消息個數是否 >= prefetch * 0.5,若是大於則補充發送一個「DELIVERED_ACK_TYPE」的確認指令;這時就開始調用onMessage()方法,若是是同步(receive),那麼即返回message。上述過程,和其餘確認模式沒有任何特殊的地方。

    當開發者決定事務能夠提交時,必須調用session.commit()方法,commit方法將會致使當前session的事務中全部消息當即被確認;事務的確認過程當中,首先把本地的deliveredMessage隊列中還沒有確認的消息所有確認(STANDARD_ACK_TYPE);此後向broker發送transaction提交指令並等待broker反饋,若是broker端事務操做成功,那麼將會把本地deliveredMessage隊列清空,新的事務開始;若是broker端事務操做失敗(此時broker已經rollback),那麼對於session而言,將執行inner-rollback,這個rollback所作的事情,就是將當前事務中的消息清空並要求broker重發(REDELIVERED_ACK_TYPE),同時commit方法將拋出異常。

    當session.commit方法異常時,對於開發者而言一般是調用session.rollback()回滾事務(事實上開發者不調用也沒有問題),固然你能夠在事務開始以後的任什麼時候機調用rollback(),rollback意味着當前事務的結束,事務中全部的消息都將被重發。須要注意,不管是inner-rollback仍是調用session.rollback()而致使消息重發,都會致使message.redeliveryCounter計數器增長,最終都會受限於brokerUrl中配置的"jms.redeliveryPolicy.maximumRedeliveries",若是rollback的次數過多,而達到重發次數的上限時,消息將會被DLQ(dead letter)。

    INDIVIDUAL_ACKNOWLEDGE : 單條消息確認,這種確認模式,咱們不多使用,它的確認時機和CLIENT_ACKNOWLEDGE幾乎同樣,當消息消費成功以後,須要調用message.acknowledege來確認此消息(單條),而CLIENT_ACKNOWLEDGE模式先message.acknowledge()方法將致使整個session中全部消息被確認(批量確認)。

    **結語:**到目前爲止,咱們已經已經簡單的瞭解了ActiveMQ中消息傳送機制,還有JMS中ACK策略,重點分析了optimizeACK的策略,但願開發者可以在使用activeMQ中避免一些沒必要要的錯誤。本文若有疏漏和錯誤之處,請各位不吝賜教,特此感謝。

相關文章
相關標籤/搜索