2010-08-09 166 views
9

我有一個JMS客戶端,可以在接收到消息時遠程系統ssh(並在那裏做各種事情 - 與問題無關)。數百個這樣的消息可能會在短時間內到達,需要儘快處理。JMS消息重新傳遞延遲

但是,當收到消息時某些遠程系統不可用,因此它們應該推遲到稍後(例如1小時左右)。最好的解決方案是將消息放回到隊列中,設置一些「延遲」值,這將告訴JMS代理不要在一個小時內再次傳遞消息。

什麼是不好的:在接收線程中睡眠並在一小時後喚醒。由於消息消費池是有限的(例如,8個可用的連接),具有8個不可到達的系統會不必要地阻塞整個處理,這是不可接受的。

對於這樣的「延遲」值,我沒有找到消息或隊列本身的設置,它是否存在?

解決方法是使用第二個隊列將消息存儲到不可到達的系統,然後分別進行處理。但它不是一個非常優雅的解決方案,並且需要額外的編程。也許有更好的辦法。

回答

3

這不可能通過JMS API之前的JMS API來實現。作爲一般規則,消息傳輸經過優化以儘可能快地傳遞消息,並且缺少調度程序以便在某些任意間隔內保存重傳消息。假設有一個傳輸提供者具有這樣的功能,那麼你寫的任何東西都會被綁定到該傳輸提供者,因爲雖然代碼可能符合JMS,但應用程序本身依賴於這個特定於供應商的行爲。

請參閱@ Shashi的答案,該答案適用於支持JMS 2.0的MQ版本。

+0

這是一個翔實的答案,謝謝。 – egbokul 2010-08-10 06:54:41

+1

我認爲這隻適用於JMS 1.0? (Cf Shashi的回答) – jpaugh 2016-07-18 20:33:55

+1

是。新的MQ JMS類支持延遲交付。 – 2016-07-19 02:33:41

4

在這種情況下,使用容器管理事務。事務在容器調用onMessage方法時開始,如果onMessage方法成功完成(當拋出RuntimeException時將失敗,或從MessageDrivenBean上下文調用setRollBackOnly),該事務將被提交。您還可以配置重新發送間隔和最大重新發送次數。

如果您在GlassFish服務器上使用OpenMQ,則可以在ejb-jar.xml描述符中配置它。在ejb-jar.xml描述符中,設置屬性endpointExceptionRedeliveryInterval(以毫秒爲單位)和endpointExceptionRedeliveryAttempts(將消息發送到死信息隊列之前重新傳遞的次數)。這裏是一個例子:

<?xml version="1.0" encoding="UTF-8"?> 
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd" 
    version="3.1">  
    <enterprise-beans> 
     <message-driven> 
      <ejb-name>EjbName</ejb-name> 
      <ejb-class>com.example.MyMessageDrivenBean</ejb-class> 
      <messaging-type>javax.jms.MessageListener</messaging-type> 
      <transaction-type>Container</transaction-type> 
      <activation-config> 
       <activation-config-property> 
        <activation-config-property-name>destination</activation-config-property-name> 
        <activation-config-property-value>someQueue</activation-config-property-value> 
       </activation-config-property> 
       <activation-config-property> 
        <activation-config-property-name>destinationType</activation-config-property-name> 
        <activation-config-property-value>javax.jms.Queue</activation-config-property-value> 
       </activation-config-property> 

       <activation-config-property> 
        <activation-config-property-name>endpointExceptionRedeliveryInterval</activation-config-property-name> 
        <activation-config-property-value>5000</activation-config-property-value> 
       </activation-config-property> 
       <activation-config-property> 
        <activation-config-property-name>endpointExceptionRedeliveryAttempts</activation-config-property-name> 
        <activation-config-property-value>4</activation-config-property-value> 
       </activation-config-property> 
      </activation-config> 
     </message-driven> 
    </enterprise-beans> 
</ejb-jar> 

而在消息驅動bean內部拋出一個RuntimeException標記爲失敗,消息將返回到隊列。

這裏也有WebLogic Server的配置屬性: http://docs.oracle.com/cd/E12839_01/apirefs.1111/e13952/pagehelp/JMSjmstemplatesjmstemplateconfigredeliverytitle.html

+0

也可以使用sun-ejb.jar.xml。 https://docs.oracle.com/cd/E19798-01/821-1750/beaqm/index.html – 99Sono 2017-08-22 08:19:15

0

我想看看作爲問題: JMS消息消費者池可用...而 一些後端資源(即SSH)不可用以允許完成該消息。

如果您同意這一點,那麼問題就變成了消費者池爲什麼可用,因爲它無法完成消費? 如果游泳池不可用,那麼消息將堆積在隊列中......直到可用...如果可以繼續使用,本來可以的。 然後,如果是這種情況,您只需要一個監視組件來啓動/停止池,具體取決於資源是否可用。你不需要等待1小時,但只要後端不可用。在最後jms是 是一切,但不是監視工具。

9

JMS 2.0規範定義了一個「傳遞延遲」,客戶端可以爲其發送的每條消息指定一個以毫秒爲單位的傳遞延遲值。該值定義了消息傳遞時間,它是消息的傳遞延遲與它發送的GMT(對於事務發送,這是客戶端發送消息的時間,而不是事務提交的時間)的總和。

消息的傳遞時間是JMS提供者可以使消息在目標目標上可見並可用於傳遞給消費者的最早時間。在達到交付時間之前,提供商不得傳遞消息。

此功能對於上述場景非常方便。

0

在glassfish上,可以使用以下參考。

https://docs.oracle.com/cd/E19798-01/821-1794/aeooq/index.html

在上述參考你由GlassFish的支持激活屬性的列表。 例如endpointExceptionRedeliveryAttempts -
的次數,以重新傳遞消息時MDB拋出消息傳遞過程中的異常

那麼你有以下參考描述有效XML元素對太陽ejb-jar.xml中,這是由支持使用GlassFish的。 https://docs.oracle.com/cd/E19798-01/821-1750/beaqm/index.html

最後,您可以配置Mdb,如下面的代碼片段所示。

<ejb> 
    <ejb-name>MyMdbWith0MsRedeliveryDelayAndMultipleRedeliveriesMdb</ejb-name> 
    <bean-pool> 
     <steady-pool-size>1</steady-pool-size> 
     <resize-quantity>1</resize-quantity> 
     <max-pool-size>1</max-pool-size> 
    </bean-pool> 
    <mdb-resource-adapter> 
     <activation-config> 
      <activation-config-property> 
       <activation-config-property-name>endpointExceptionRedeliveryAttempts</activation-config-property-name> 
       <activation-config-property-value>1000</activation-config-property-value> 
      </activation-config-property> 
      <activation-config-property> 
       <activation-config-property-name>endpointExceptionRedeliveryAttempts</activation-config-property-name> 
       <activation-config-property-value>0</activation-config-property-value> 
      </activation-config-property> 
     </activation-config>    
    </mdb-resource-adapter> 
</ejb> 

這應該確保對於這個特定的mdb,glassfish將立即傳遞一條消息,如果失敗,它會立即重試。

在您的WAR項目中,創建一個子ejb-jar.xml並將其放在WEB-INF/sun-ejb-jar.xml下。

如果您使用的是maven,這將是war項目組件中的src/main/webapp/WEB-INF/sub-ejb-jar.xml路徑。