我有一个JMS客户端,可以在收到消息后ssh到远程系统(并在那里做各种事情 - 与问题无关)。数百条此类消息可能会在很短的时间内到达,需要尽快处理。
但是,当收到消息时,某些远程系统也可能无法使用,因此应将它们推迟到以后(例如1小时左右)。最好的解决方案是将消息放回队列并设置一些“延迟”值,这将告诉JMS代理不要在一小时内再次发送消息。
什么不行:睡在接收线程中并在一小时后醒来。由于消息使用者池有限(例如8个可用连接),有8个不可达的系统会不必要地阻止整个处理,这是不可接受的。
我没有为这样的“延迟”值找到消息或队列本身的设置,它是否存在?
解决方法是使用第二个队列将消息存储到无法访问的系统,并单独处理这些消息。但它不是一个非常优雅的解决方案,需要额外的编程。也许有更好的方法。
答案 0 :(得分:9)
JMS 2.0规范定义了一个"传递延迟"客户端可以为其发送的每条消息指定传递延迟值(以毫秒为单位)。此值定义消息传递时间,即消息的传递延迟和发送的GMT之和(对于事务发送,这是客户端发送消息的时间,而不是事务提交的时间)。
消息的传递时间是JMS提供者可以在目标目的地上显示消息并可用于传递给消费者的最早时间。提供商不得在达到交付时间之前发送消息。
此功能对于上述方案非常方便。
答案 1 :(得分:4)
在这种情况下,使用容器管理事务。该事务在容器调用onMessage
方法时启动,如果onMessage
方法成功完成则提交(当抛出RuntimeException或从MessageDrivenBean调用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
答案 2 :(得分:3)
通过JMS 2.0之前的JMS API无法做到这一点。作为一般规则,消息传输被优化以尽可能快地传递消息,并且缺少调度器以在某个任意间隔保持用于重新传递的消息。假设有一个确实具有此类功能的传输提供程序,那么您编写的任何内容都将绑定到该传输提供程序,因为尽管代码可能符合JMS,但应用程序本身将依赖于此特定于供应商的行为。
请参阅@ Shashi的答案,以获得适用于支持JMS 2.0的MQ版本的答案。
答案 3 :(得分:0)
我会把问题看作: jms消息消费者池可用...同时 某些后端资源(即ssh)不可用于完成消息。
如果您同意这一点,那么问题就变成为什么消费者池可用,因为它无法完成消费? 如果您没有可用的池,那么消息将堆积在队列中....直到可用...并且可以在可用时继续。 然后,如果是这种情况,您只需要一个监视组件来启动/停止池,具体取决于资源是否可用。你不需要等待1小时,但只要后端不可用。 最后,jms是一切,但不是监控工具。
答案 4 :(得分:0)
在玻璃鱼上,可以使用以下参考文献。
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路径。
答案 5 :(得分:0)