rabbitmq 重复ACK致使消息丢失

rabbitmq 重复确认致使消息丢失

背景

rabbitmq 在应用场景中,大多采用工做队列 work-queue的模式。html

在一个常见的工做队列模式中,消费者 worker 将不断的轮询从队列中拉取最新消息,当队列负载压力增大时容许添加多个worker 进行处理。
然而执行一个任务可能须要至关的时长,这是由业务特性所决定的;若是 worker执行任务过程当中出现异常甚至宕机,此时消息便会丢失,这是简单消息队列难以解决的问题。网络

rabbitmq 采用了消息确认机制来防止此类问题,在该机制中,worker须要向 MQ Server 返回 ACK响应以表示消息已确认处理;
在如下状况下,rabbitmq 会对消息进行从新投递:
1 client 未响应ACK, 主动关闭 Channel;
2 client 未响应ACk, 网络异常断开;测试

消息的重发机制没有超时限制,只要client 不响应ACK,那么会一直投递;
若是启用了消息持久化机制,那么消息将有进一步的保障。this

问题描述及分析

1 客户端为简化应答处理,能够设置自动应答选项,如:编码

boolean autoAck = false; channel.basicConsume(TASK_QUEUE_NAME, autoAck, consumer);

2 若是不启用自动应答,须要应用代码手动进行应答:spa

try { doWork(message); } finally { logger.info(" xxx work done"); channel.basicAck(envelope.getDeliveryTag(), false); }

3 当两种方案同时存在日志

因为客户端的编码失误,先启用了自动应答选项,又在应用代码执行了应答的代码:
// enable autoAck boolean autoAck = true; consumerChannel.basicConsume(queueName, autoAck, this); //... // snipper from Consumer.handleDelivery method // send ack to server try { consumerChannel.basicAck(deliveryTag, true); } catch (Exception e) { }

多了一次确认,应用代码貌似一切如常。 但在频繁进行消息收发测试时发现 消息存在随机性丢失处理的状况!
检查 rabbitmq server日志发现如下异常:code

{amqp_error,precondition_failed,"unknown delivery tag 1",'basic.ack'} ... {amqp_error,precondition_failed,"unknown delivery tag 1",'basic.ack'} ... {amqp_error,precondition_failed,"unknown delivery tag 1",'basic.ack'} ...

提示未知的 delivery tag=1,该字段为MQ server 用于消息确认的标记,服务端因没法识别而打印错误。
另一个现象则是,连续收发消息 5次,其中丢失消息处理1次,而 rabbitmq server错误日志出现 4次!server

通过分析,发现问题缘由所在:
rabbitmq 为每个channel维护了一个delivery tag的计数器,这里采用正向自增,新消息投递时自增,当消息响应时自减;
在连续收发的场景中,因为消息发送的间隔较短,部分消息因 consumer的重复确认被rabbitmq 当作已处理而丢弃。htm

解决方案

取消consumer 的自动应答机制,仅保留手动应答的处理,问题解决。

参考资料

关于 rabbitmq 消息确认机制:
http://www.rabbitmq.com/confirms.html#when

相关文章
相关标签/搜索