最近在使用redis时发现总有一些消息队列中存在大量的处于
unacked
状态的消息,一般来说,如果队列中
ready
状态的消息数比较多,可以认为是消费者的处理能力不足,可以通过增加消费者来解决,而
unacked
消息存在基本是有以下两点原因:
-
消费者取走消息后没有及时做消息确认,对于开启手动确认机制的,不进行ack则消息会一直以
unacked
状态留在队列中。 -
消费者处理能力不足。生产者投放消息的速度较快,当消费者按照
prefetch_count
设置的值取走相应数量的消息时,这些消息都会暂时处于
unacked
状态。
我司目前对redis的使用是基于Celery的,Celery对消息确认采用的是
early ack
,即在消费者执行task之前,就已经向redis发送确认消息了,哪怕task产生异常也不会受到任何影响。所以队列中
unacked
的消息不是自定义task异常产生的。若是消费者处理能力不足,则
ready
状态的消息应该会有一定的堆积,但是也没有观察到这点,所以不能判定为消费者能力的限制。
有没有可能是消费者挂掉导致的呢?消费者挂掉后,
unacked
的消息会变成
ready
状态的消息重新放在队列中,待下次消费者启动后可以直接读取,所以也不会是这个原因。
通过以上分析,并没有发现消费者有何问题,只能尝试从生产者来分析了。Celery有两种生产消息的方式,
delay
和
apply_async
。
-
delay
直接向队列中投递消息,消费者立时可取,任务立即可执行 -
apply_async
投递的定时消息,消费者立时可取,任务定时执行
任务定时执行是Celery的功能,原理是amqp消息的header中存放了任务的执行时间,Celery会根据这个时间来执行任务,哪怕消费者挂掉了,当再次启动时,定时任务仍然能够正常执行。
但是,定时任务的ack消息并不是在消费者取走消息后就发送的,只有在任务真正执行前才会发送。这也是为什么
clock_queue
中存在这么多
unacked
消息的原因,不是真的出了什么问题,而是有这么多任务等待被执行呢。