activeMQ中的消息重试与死信队列

前言

我们在使用activemq时难免有一些消费失败的消息,拿这些消息怎么办?他们会在哪里呢?

消费重试机制

在消息的消费过程中,如果消息未被签收或者签收失败,是会导致消息重复消费的,但如果消息一直签收失败,那是不是就会被无限次的消费呢?答案是否定的。

一条消息签收不成功,消息服务器就会认为该消费者没有消费过这条消息,就会再次将这条消息传送给该消费者供它消费。至于会传送几次取决于我们定义的消费重试机制。很显然消费重试机制是针对消费者端的。

哪些情况下会发生消息的重复消费呢?其实就是客户端消息签收失败的情况下,这包括但不限于以下情况:
1.消费者端开启事务,但最终事务回滚而未提交,或者在提交之前关闭了连接而提交失败
2.需要手动签收(CLENT_ACKNOWLEDGE)的消息,消费者端在签收之后又调用了 session.recover();
 在默认情况下,当消息签收失败时ActiveMQ消息服务器会继续每隔1秒钟向消费者端发送一次这个签收失败的消息,默认会尝试6次(加上正常的1次共7次),如果这7次消费者端全部签收失败,则会给ActiveMQ服务器发送一个“poison ack”,表示这个消息不正常(“有毒”),这时消息服务器不会继续传送这个消息给这个消费者,而是将这个消息放入死信队列(DLQ,即Dead Letter Queue)
消费重试机制的默认相关配置如下:

å¨è¿éæå¥å¾çæè¿°

配置说明:
  1、collisionAvoidanceFactor:设置防止冲突范围的正负百分比,只有启用useCollisionAvoidance参数时才生效,也就是在延迟时间上再加一个时间波动范围,默认值为0.15
  2、maximumRedeliveries:最大重传次数,达到最大重传次数后抛出异常。值为-1时不限制次数,为0时不重传,默认值为6
  3、maximumRedeliveryDelay:最大重连时间间隔,只在useExponentialBackOff为true是有效。假设首次重连间隔10ms,倍数为2,那么第2次重连的时间间隔为20ms,第3次重连的时间间隔为40ms,当重连时间间隔大于最大重连时间间隔时,以后每次重连的时间间隔都是设置的最大重连时间间隔。默认值为-1,表示没有最大重连时间间隔
  4、initialRedeliveryDelay:初始的重发时间间隔,即正常发送签收失败后间隔多长时间进行重发,默认值为1000L
  5、redeliveryDelay:重发延迟时间,当initialRedeliveryDelay=0是有效,默认1000L
  6、useCollisionAvoidance:启用防止冲突功能,默认false
  7、useExponentialBackOff:启用指数倍数递增的方式增加重发延迟时间,默认false
  8、backOffMultiplier:重连时间间隔的递增倍数,只有值大于1和启用useExponentialBackOff参数时生效,默认为5
自定义消费重试示例:只需要在消费者端设置重试策略

ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(BROKER_URL);
//自定义消费重试机制
RedeliveryPolicy policy = new RedeliveryPolicy();
policy.setMaximumRedeliveries(3);
factory.setRedeliveryPolicy(policy);
Connection connection = factory.createConnection();
connection.start();

死信队列

ActiveMQ中引入了“死信队列”(Dead Letter Queue)的概念,在一条消息被重复发送给消息消费者端多次(默认为6次)后,若一直签收不成功,则ActiveMQ会将这条消息移入到“死信队列”。开发时可以开启一个后台线程监听这个队列(默认死信队列的名称为ActiveMQ.DLQ)中的消息,进行人工干预,也就是说死信队列的作用主要是处理签收失败的消息。
å¨è¿éæå¥å¾çæè¿°

关于死信队列的配置主要有两种:SharedDeadLetterStrategy和IndividualDeadLetterStrategy

1.SharedDeadLetterStrategy:共享的死信队列配置策略,将所有的DeadLetter保存在一个共享的队列中,这是ActiveMQ Broker端的默认策略。共享队列的名称默认为“ActiveMQ.DLQ”,可以通过“deadLetterQueue”属性来设定:在activemq.xml中的<policyEntries>节点中配置
 

<deadLetterStrategy>
	<sharedDeadLetterStrategy deadLetterQueue="DLQ-QUEUE"/>
</deadLetterStrategy>

2.IndividualDeadLetterStrategy:单独的死信队列配置策略,把DeadLetter放入各自的死信通道中。对于Queue而言,死信通道的前缀默认为“ActiveMQ.DLQ.Queue”;对于Topic而言,死信通道的前缀默认为“ActiveMQ.DLQ.Topic”。比如队列Order,那么它对应的死信通道为“ActiveMQ.DLQ.Queue.Order”。我们可以使用queuePrefix和topicPrefix来指定上述前缀:
 

<!-- 仅对与order队列起作用 -->
<policyEntry queue="order">
	<deadLetterStrategy>
		<!-- useQueueForQueueMessage属性的作用:是否将名为order的Topic中的DeadLetter也保存在该队列中,默认为true -->
		<individualDeadLetterStrategy queuePrefix="DLQ." useQueueForQueueMessage="false"/>
	</deadLetterStrategy>
</policyEntry>

注意:默认情况下,无论是Topic还是Queue,Broker都使用Queue来保存DeadLetter,即死信通道通常为Queue,不过开发时也可以指定为Topic

配置案例1:自动删除过期消息,此时对于过期的消息将不会被放入到死信队列,而是自动删除,>表示对所有队列起作用,processExpired表示是否将过期消息放入死信队列,默认为true

<!-- >表示对所有队列起作用 -->
<policyEntry queue=">">
	<deadLetterStrategy>
		<sharedDeadLetterStrategy processExpired="false"/>
	</deadLetterStrategy>
</policyEntry>

配置案例2:将签收失败的非持久消息也放入到死信队列,默认情况下,ActiveMQ不会把非持久化的死消息放入死信队列,processNonPersistent表示是否将非持久化消息放入死信队列,默认为false

<!-- >表示对所有队列起作用 -->
<policyEntry queue=">">
	<deadLetterStrategy>
		<sharedDeadLetterStrategy processNonPersistent="true"/>
	</deadLetterStrategy>
</policyEntry>

防止重复调用引发的问题

 ActiveMQ中的消息有时是会被重复消费的,而我们消费消息时大都会在拿到消息后去调用其他的方法,比如说将消息的内容解析为一个对象保存到数据库中。一旦发生消息的重复消费时就会重复保存,这是有问题的,因此我们需要考虑如何防止重复调用。其实我们是没有办法防止重复调用的,只能在重复调用时进行消息是否重复消费的校验,当然对于幂等性接口也可以不进行校验。
 那如何进行校验呢?有很多种方式,比如说我们将消费过的消息的messageId保存到数据库,每次消费消息前先到数据库中查一下该消息是否已被消费。在分布式系统中,也可以将消费过的消息放入redis中,以messageId作为key,message对象作为value(其实value不重要,当然也要看需求本身),在消费消息时先从redis中查找该消息是否已被消费。
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值