2011-06-21 115 views
50

在我的JMS應用程序上,我們使用生產者上的臨時隊列來接收消費者應用程序的回覆。ActiveMQ:如何在使用臨時隊列時處理代理故障轉移

我面對我的結束正是同樣的問題在這個線程中提到:http://activemq.2283324.n4.nabble.com/jira-Created-AMQ-3336-Temporary-Destination-errors-on-H-A-failover-in-broker-network-with-Failover-tt-td3551034.html#a3612738

每當我重新啓動我的網絡中的任意經紀人,我得到很多象這樣的錯誤在我的消費者應用程序日誌嘗試發送回覆臨時隊列:

javax.jms.InvalidDestinationException: 
    Cannot publish to a deleted Destination: temp-queue://ID:... 

然後,我看到有加里的反應表明使用

jms.watchTopicAdvisories=false 

爲AU rl param在客戶端brokerURL上。我立即用這個附加參數更改了我的客戶端代理網址。

javax.jms.JMSException: 
    The destination temp-queue: 
    //ID:client.host-65070-1308610734958-2:1:1 does not exist. 

我使用ActiveMQ的5.5版本:但是現在,當我重新啓動我的經紀人在網絡這個倒換測試,我看到了這樣的錯誤。而我的客戶的經紀人URL看起來是這樣的:

failover:(tcp://amq-host1:61616,tcp://amq-host2.tred.aol.com:61616,tcp://amq-host3:61616,tcp://amq-host4:61616)?jms.useAsyncSend=true&timeout=5000&jms.watchTopicAdvisories=false 

另外這裏是4個經紀人我的一個ActiveMQ的XML配置: amq1.xml

能有人在這裏請看看這個問題,並建議我有什麼錯誤,我我正在製作這個設置。

更新:

爲了進一步澄清,我怎麼在我的代碼做請求 - 響應:

  1. 我已經使用每生產目標(即臨時隊列),並將其設置在答覆向每封郵件的標題。
  2. 我已經在JMSCorrelationID標頭中發送了每個消息的唯一關聯標識符。
  3. 據我所知即使駱駝和Spring也使用臨時隊列來請求響應機制。唯一的區別是Spring JMS實現爲每條消息創建並銷燬臨時隊列,而我爲生產者的生命週期創建臨時隊列。當客戶端(生產者)應用程序關閉時,此臨時隊列會被銷燬,或者AMQ代理在意識到沒有活動的生產者與此臨時隊列連接時會被銷燬。
  4. 我已經在Producer端的每條消息上設置了消息到期時間,以便消息不會在隊列中滯留太久(60秒)。
+1

新的'JMSException'是剛登錄還是拋入客戶端代碼?另外,客戶端發送給代理的每條消息是否拋出異常,或者故障轉移完成時異常是否停止? (即只有在客戶端沒有連接的時候拋出異常?) – Bringer128

+2

[似乎是](http://activemq.apache.org/advisory-message.html#AdvisoryMessage-Disablingadvisorymessages)有幾件事情您需要在XML配置中添加'jms.watchTopicAdvisories = false',即'',並靜態配置您的網絡。 (你的amq1.xml文件給我404 Not Found) – opyate

+1

@ Bringer128:感謝您的評論。該JMS異常在重新連接後生產者連接的其他AMQ代理上引發。而且一旦發生這種情況,JMS生產者只是停止接收來自消費者的任何響應,因爲AMQ代理不能將回復發回給具有上述JMS異常的生產者。 – anubhava

回答

24

有一個代理屬性,org.apache.activemq.broker.BrokerService#cacheTempDestinations,應該有助於故障轉移:大小寫。 在xml配置中將其設置爲true,並且在客戶端斷開連接時,不會立即刪除臨時目標。 快速故障轉移:重新連接將能夠再次生成和/或從臨時隊列中消耗。

有一個計時器任務基於timeBeforePurgeTempDestinations(缺省5秒)來處理緩存刪除。

但有一點需要注意,我在activemq-core上看不到任何使用該屬性的測試,所以我不能保證這個。

+1

如果您發現我在我的問題中寫道:'每當我重新啓動我的網絡中的任意經紀人'。所以我想知道如果代理本身正在重新啓動,這個代理屬性'cacheTempDestinations'會有什麼效果嗎? – anubhava

+4

當您重新啓動代理時,任何臨時目標將連同任何掛起的消息都將丟失。經紀人沒有爲臨時目的地維護持續狀態。 – gtully

+0

我們能做的最好的是:1)支持故障轉移:重新連接到現有的代理。 2)如果在代理之間進行網絡分區,或者在發送對臨時目標的回覆之前代理髮生故障,則允許自動創建臨時目標。 – gtully

9

在請求回覆場景中的請求者(生產者)所連接的代理上創建臨時隊列。它們是從javax.jms.Session創建的,所以在該會話斷開連接時,無論是因爲客戶端斷開還是代理失敗/故障轉移,這些隊列都會永久消失。其他經紀人都不會理解當你的一個消費者試圖回覆這些隊列時的含義;因此你的例外。

這需要思維模式的架構轉變,假設您想要處理故障轉移並保留所有消息。以下是您可以攻擊該問題的一般方法:

  1. 您的回覆標題應引用特定於請求者進程的隊列:例如, queue:response.<client id>。如果您的客戶端數量有限,則客戶端ID可能是標準名稱,如果您有大量客戶端,則客戶端ID可能是UUID。
  2. 出站消息應該設置一個關聯標識符(簡單來說,可以讓你將請求與響應關聯起來 - 請求者畢竟可以同時發出多個請求)。這在JMSCorrelationID標題中設置,並且應該從請求中複製到響應消息。
  3. 請求者需要在該隊列上設置一個偵聽器,該偵聽器將根據該相關ID將消息主體返回給請求的線程。有一些多線程代碼需要爲此編寫,因爲您需要手動管理類似於相關ID的映射到原始線程(可能通過Futures)。

這是一個類似於Apache Camelrequest-response over messaging的方法。

需要注意的一點是,當客戶端執行時,隊列不會消失,因此您應該設置一個時間來響應消息,以便從代理中刪除(如果它沒有被使用)否則你會收到未消費的消息。您還需要設置一個dead letter queue strategy to automatically discard expired messages

+0

感謝你的詳細答案。我在我的問題中添加了更新部分,以迴應您的所有觀點。你是否有機會建議我不要使用每個生產者的臨時隊列**? – anubhava

+0

這是正確的,臨時隊列不是爲故障轉移而設計的 - 它們是客戶端和代理之間通過會話的合同;該會話位於單獨的連接上。 Camel在默認情況下通過臨時隊列進行請求回覆,但這對於所列出的原因通常是不切實際的,並且因此支持靜態隊列作爲回退來滿足服務質量(儘管它使用隊列中的JMS選擇器進行關聯,由於性能原因而被避免)。 –

+0

IMO假定'臨時隊列不是爲故障轉移而設計的'是不正確的。同樣,如果沒有使用臨時隊列,那麼如果你事先不知道所有的生產者,你怎麼能擁有「一個特定於請求者進程的隊列」,或者換句話說,每個生產者。 – anubhava

相關問題