原文地址
http://activemq.apache.org/message-redelivery-and-dlq-handling
概述
下面任何一种情况的发生,消息都将重新发送给客户端:
1、使用事务会话情况下调用rollback()
2、事务会话在commit()之前关闭
3、当前会话使用了CLIENT_ACKNOWLEDGE 并且调用Session.recover()
4、客户端连接超时(或许是代码执行的时间比配置的超时时间长)
broker在它的BrokerInfo 命令包中更喜欢使用默认的传递策略去传输到客户端连接。但是客户端可以通过设置 ActiveMQConnection.getRedeliveryPolicy()方法重新传递策略:
RedeliveryPolicy policy = connection.getRedeliveryPolicy();
policy.setInitialRedeliveryDelay(500);
policy.setBackOffMultiplier(2);
policy.setUseExponentialBackOff(true);
policy.setMaximumRedeliveries(2);
一旦消息重试次数超过 Redelivery Policy中maximumRedeliveries配置的值,将发送一个“Poison ACK”到broker,让它知道这个消息已经被视为是一个毒药丸。然后broker接收到这条消息并且把消息发送给死信队列,以便以后对其进行分析。
ActiveMq中默认的死信队列叫做 ActiveMQ.DLQ;全部无法重试的消息将被发送到这个队列中,这可能很难管理。因此你能从activemq.xml中的目的地映射中设置individualDeadLetterStrategy,这允许你给队列或者是主题指定一个特定的死信队列前缀。如果你愿意,你可以使用通配符来应用这个策略,以便他们可以获取到他们自己的死信队列,如下面列子展示的:
<broker>
<destinationPolicy>
<policyMap>
<policyEntries>
<!-- Set the following policy on all queues using the '>' wildcard -->
<policyEntry queue=">">
<deadLetterStrategy>
<!--
Use the prefix 'DLQ.' for the destination name, and make
the DLQ a queue rather than a topic
-->
<individualDeadLetterStrategy queuePrefix="DLQ." useQueueForQueueMessages="true"/>
</deadLetterStrategy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
</broker>
有关策略选项的更多细节信息,见 Redelivery Policy章节。
自动丢弃过期消息
有些人只是需要丢弃过期的消息而不是发送到DLQ(死信队列),也就是说完全跳过死信队列。这样就简化了死信队列的管理,因此你不需要通过大量的过期的消息去查找真正问题消息。告诉ActiveMq丢弃过期的消息,在死信策略的中将processExpired属性设置成false:
<broker>
<destinationPolicy>
<policyMap>
<policyEntries>
<!-- Set the following policy on all queues using the '>' wildcard -->
<policyEntry queue=">">
<!--
Tell the dead letter strategy not to process expired messages
so that they will just be discarded instead of being sent to
the DLQ
-->
<deadLetterStrategy>
<sharedDeadLetterStrategy processExpired="false" />
</deadLetterStrategy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
</broker>
将非持久化消息放入死信队列中
默认情况下,ActiveMQ不会将没有投递的非持久化的消息放入死信队列中。这种行为的基本原理是,如果应用不太关心消息的持久性,那么记录没有投递的消息是几乎没有价值。如果你想把非持久化的消息放入死信队列中,那么你应该在死信策略中设置processNonPersistent=“true”
<broker>
<destinationPolicy>
<policyMap>
<policyEntries>
<!-- Set the following policy on all queues using the '>' wildcard -->
<policyEntry queue=">">
<!--
Tell the dead letter strategy to also place non-persisted messages
onto the dead-letter queue if they can't be delivered.
-->
<deadLetterStrategy>
<sharedDeadLetterStrategy processNonPersistent="true" />
</deadLetterStrategy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
</broker>
在死信队列中设置消息的过期时间
默认情况,ActiveMQ中发到死信队列的消息时永远不会过期的,但是从ActiveMQ5.12开始,deadLetterStrategy支持expiration属性,它的值得单位是毫秒
在如何应用这个功能是有选择的,尤其是不要在默认或者包含通配符策略的条目上设置过期时间,将过期时间应用到你的死信队列上。
如果一个死信队列条目过期或者转发到相同的或者其他死信队列上,如果禁用了策略审计或者超过了它的滑动窗口,这将产生一个可能会产生问题的循环。
<broker>
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue="QueueWhereItIsOkToExpireDLQEntries">
<deadLetterStrategy>
<.... expiration="300000"/>
</deadLetterStrategy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
</broker>
消息审计
死信队列默认开启了一个消息审计功能。这样可以阻住重复的消息添加到已配置的死信队列中。从5.15.0开始,审计限制可以通过maxProducersToAudit和maxAuditDepth属性来配置。设置**enableAudit=“false”**可以关闭审计功能。
死信队列丢弃插件
从5.9开始,一个目的地的policyEntry支持一个deadLetterStrategy丢弃
<deadLetterStrategy>
<discarding/>
</deadLetterStrategy>
这功能和插件相同,但是它基于每一个目的地。插件的匹配是基于正则表达式的,比针对于目的地的匹配是更强大的。因此在某些情况下这个插件是很有用的。
一个非常简单且有用的broker插件。这个插件可以配置队列或者主题(全部或者匹配基于Java SE正则表达式)去丢弃已经发送到死信队列中的消息。这个是非常有用的当使用到恒定待处理消息限制策略或者是其它驱逐规则,而且你不希望在引用另一个消费者清除死信队列的开销。
下面的例子是删除所有内容的基本配置
<beans>
<broker>
<plugins>
<discardingDLQBrokerPlugin dropAll="true" dropTemporaryTopics="true" dropTemporaryQueues="true"/>
</plugins>
</broker>
</beans>
下面是一个稍微复杂的示例:
<beans>
<broker>
<plugins>
<discardingDLQBrokerPlugin dropOnly="MY.EXAMPLE.TOPIC.29 MY.EXAMPLE.QUEUE.87" reportInterval="1000"/>
</plugins>
</broker>
</beans>
- 注意目的地的名称使用空格分隔
- reportInterval属性用来表示输出丢弃了多少条消息的频率-使用0来禁用
下面是一个更复杂的例子
<beans>
<broker>
<plugins>
<discardingDLQBrokerPlugin dropOnly="MY.EXAMPLE.TOPIC.[0-9]{3} MY.EXAMPLE.QUEUE.[0-9]{3}" reportInterval="3000"/>
</plugins>
</broker>
</beans>
- 注意目的地的命名使用正则表达式。匹配目的地名称结尾是数字000…999的
更多信息见DiscardingDLQBrokerPlugin and the DiscardingDLQBroker的源码。
broker重新投递 (v5.7)
通常,消费者会处理重新投递,以便当消息以飞行状态下出现在broker上时它能够维护消息顺序。这意味着重新投递是限制单个消费者除非消费者已经终结了。这样broker就不知道重新投递。使用broker的重新投递可能在延迟后重新发送需要重新投递的消息。这由broker插件实现,通过调度器重新投递来处理死信队列。当全部消息的顺序是不重要的时候和通过放置和消费者之间的负载分配,它是非常有用的。通过broker的重新投递,失败投递到消费者的消息可以立即重新分发。
通过XML配置启用该特性:
<broker schedulerSupport="true">
<plugins>
<redeliveryPlugin fallbackToDeadLetter="true"
sendToDlqIfMaxRetriesExceeded="true">
<redeliveryPolicyMap>
<redeliveryPolicyMap>
<redeliveryPolicyEntries>
<!-- a destination specific policy -->
<redeliveryPolicy queue="SpecialQueue"
maximumRedeliveries="4"
redeliveryDelay="10000"/>
</redeliveryPolicyEntries>
<defaultEntry>
<!-- the fallback policy for all other destinations -->
<redeliveryPolicy maximumRedeliveries="4"
initialRedeliveryDelay="5000"
redeliveryDelay="10000"/>
</defaultEntry>
</redeliveryPolicyMap>
</redeliveryPolicyMap>
</redeliveryPlugin>
</plugins>
</broker>
常见的重新投递策略已被扩展为使用匹配目的地。fallbackToDeadLetter控制是当前目的地没有匹配的重发策略时的操作,默认为true,因此会进行常规的DLQ处理。sendToDlqIfMaxRetriesExceeded控制超过重试限制时的操作,默认为true,因此会进行常规的DLQ处理。当为false时,消息将被丢弃。
需注意:ActiveMq的schedulerSupport必须开启对于这个特性的使用