9

我有一个 JMS 客户端,它可以在收到消息后通过 ssh 连接到远程系统(并在那里做各种事情——与问题无关)。可能会有数百条此类消息在短时间内到达,需要尽快处理。

但是,也有可能某些远程系统在收到消息时不可用,因此应该推迟到更晚(例如 1 小时左右)。最好的解决方案是将消息放回队列并设置一些“延迟”值,这将告诉 JMS 代理不要在一小时内再次尝试传递消息。

什么不好:在接收线程中休眠并在一个小时后醒来。由于消息消费者池是有限的(例如 8 个可用连接),因此具有 8 个不可访问系统会不必要地阻塞整个处理,这是不可接受的。

我没有找到这样一个“延迟”值的消息或队列本身的设置,它存在吗?

一种解决方法是使用第二个队列将消息存储到无法访问的系统,并分别处理这些消息。但这不是一个非常优雅的解决方案,并且需要额外的编程。也许有更好的方法。

4

6 回答 6

9

JMS 2.0 规范定义了一个“传递延迟”,客户端可以为它发送的每条消息指定一个传递延迟值(以毫秒为单位)。这个值定义了一个消息传递时间,它是消息的传递延迟和它发送的 GMT 的总和(对于事务发送,这是客户端发送消息的时间,而不是事务提交的时间)。

消息的传递时间是 JMS 提供者可以使消息在目标目的地上可见并且可用于传递给消费者的最早时间。提供者不得在到达传递时间之前传递消息。

对于上述场景,此功能非常方便。

于 2014-04-25T06:06:41.380 回答
4

在这种情况下,使用容器管理事务。事务onMessage在容器调用方法时启动,如果方法成功完成则提交事务(当抛出 RuntimeException 或从 MessageDrivenBean 上下文调用时,事务onMessage将失败)。setRollBackOnly您还可以配置重新发送间隔和最大重新发送次数。

如果您将 OpenMQ 与 Glassfish 服务器一起使用,则可以在 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

于 2013-05-22T08:30:52.407 回答
3

这在 JMS 2.0 之前的 JMS API 中是不可能的。作为一般规则,消息传输经过优化以尽可能快地传递消息,并且缺少调度程序来保存消息以在某个任意时间间隔重新传递。假设有一个传输提供程序确实具有这样的功能,那么您编写的任何内容都将绑定到该传输提供程序,因为尽管代码可能与 JMS 兼容,但应用程序本身将依赖于这种特定于供应商的行为。

有关适用于支持 JMS 2.0 的 MQ 版本的答案,请参阅@Shashi 的答案。

于 2010-08-09T19:23:23.133 回答
0

我会将问题视为:jms 消息消费者池可用......而某些后端资源(即 ssh)无法用于完成消息。

如果您同意这一点,那么问题就变成了为什么消费者池因为它无法完成消费而可用?如果您的池不可用,那么消息将堆积在队列中......直到可用......并且在可用时可以继续。然后,如果是这种情况,您只需要一个监视组件来启动/停止池,具体取决于资源是否可用。您不必等待 1 小时,但只要后端不可用。最后,jms 是一切,但不是监控工具。

于 2014-04-24T22:38:00.760 回答
0

在 glassfish 上,可以使用以下参考资料。

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

在上述参考资料中,您将获得 glassfish 支持的激活属性列表。例如,endpointExceptionRedeliveryAttempts -
当 MDB 在消息传递期间抛出异常时重新传递消息的次数

然后,您将获得以下参考,该参考描述了要在 glassfish 支持的 sun-ejb-jar.xml 上使用的有效 xml 元素。 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 项目中,创建一个 sub-ejb-jar.xml 并将其放在 WEB-INF/sun-ejb-jar.xml 下。

如果您使用的是 maven,这将是您在 war 项目组件上的 src/main/webapp/WEB-INF/sub-ejb-jar.xml 路径。

于 2017-08-22T08:55:44.170 回答
0

再会!

确切地说,对于 Oracle WebLogic Server,有两个参数:

  • Redelivery Delay Override - 在Queue级别上,以及
  • Default Redelivery Delay - 在连接工厂级别。
于 2018-04-04T11:46:47.833 回答