常见的消息队列,包括RabbitMQ,ActiveMQ,Kafka。
他们的特点通过生产者消费者模型,来实现多种效果,包括限流削峰,异步处理,解耦等。当然,会适当的增加系统复杂度,在数据一致性上容易出问题。
接下来是针对RabbitMQ 消息队列(后续简称消息队列)的ACK 机制做一个记录。
ACK 机制,是一种消息确认机制。体现在生产者发送消息和消费者获取消息上,即:
消息队列发送ACK 消息,告诉生产者,已经接收到消息;
消费者发送ACK 消息,告诉消息队列,已经接收到消息;
通过ACK 的应用场合,可以简单的看出,ACK 机制可以防止消息在出入队列时丢失,有很多类似的确认机制,比如TCP 连接中的握手机制。
以消费者从消息队列中获取消息为例:
消费者建立和消息队列的连接,并且通过队列的绑定,等待消息从消息队列中推送过来。
消息队列从ready 状态的消息中,找到满足规则的消息发送给消费者,同时将此条消息状态修改为unack。
消费者收到消息后,控制何时发送 ack 消息至消息队列。
消息队列收到ack 消息,对该条 unack 的消息进行删除。如果未能收到ack 机制且与消费者的连接断开,消息重新回到ready状态,等待下一个消费者获取消息。
我们所使用的,关于rabbitMQ 的SDK,在ACK 机制上,通常是默认回发的,即,当消费者接收到一条信息时,SDK 默认在接收到消息时,立马发送ACK 信息。此时,消息队列将此条消息删除(生产者发送消息至消息队列与之类似)。
但是,当消费者及时获得消息,却未能正确、成功消费消息时,比如消费者进程突然崩溃,带来的效果同样是消息丢失。
所以合理的、定制化的使用ACK 机制,可以进一步确保消息不丢失:
拒绝默认回发ACK,采用根据消息,先进行消费,等待消费正确结束后,再回发ACK 消息,告诉消息队列:成功消费此条消息。
此期间,依照ACK 的运行机制,消息队列会把此条消息从 ready 状态改做unack 状态,不允许别的消费者消费,直到收到ack 消息,删除此条信息,或者未收到ack消息(通常消费者连接会断开),状态回到ready,等待被别的消费者消费。
关键点:
以上的逻辑,可以确保消息一定被消费,但是需要注意代码逻辑以及和消息队列的连接不要断开。举例子说明:
正确处理后,忘记发送ack 消息,将导致消息被多次消费或者一直处于unack 状态,消息队列内存溢出或者消息被多次消费。
不合理的线程、进程使用,导致消息在消费期间,消费者和消息队列的连接断开,导致消息重回ready 状态被重新且多次消费。
这里主要针对的是耗时的,计算密集型消息任务。




