RabbitMQ管理台Purge大量消息堆积队列风险

RMQ线上集群(v3.6.12)purge一个大量消息堆积(100W+)的队列时,有可能导致客户端报大量发送超时异常。

原因

purge操作实际发生的事情(rabbit_amqqueue_process.erl):

handle_call(purge, _From, State = #q{backing_queue = BQ,
backing_queue_state = BQS}) ->
{Count, BQS1} = BQ:purge(BQS),
...

BQ默认对应 rabbit_variable_queue 模块:

msg_store_remove(MSCState, IsPersistent, MsgIds) ->
with_immutable_msg_store_state(
MSCState, IsPersistent,
fun (MCSState1) ->
rabbit_msg_store:remove(MsgIds, MCSState1)
end).

也就是purge操作,最终是由rabbit_msg_store进程来进行操作的。

rabbit_msg_store 进程,每个节点只有一个,因此容易成为瓶颈。

rabbitmq 进程间通信采用 credit_flow 机制,一般一条持久化消息发送到持久化队列的大致过程如下:

rabbit_reader → rabbit_channel → rabbit_amqqueue_process → rabbit_msg_store

当 rabbit_msg_store 进程忙于大量消息的purge操作时,不能及时处理其上游 rabbit_amqqueue_process 的消息,这会导致其上游 rabbit_amqqueue_process 很快耗光其 credit 值,从而造成 flow。同理,

当 rabbit_amqqueue_process 进程由于 flow 被 block 住,不能及时处理其上游 rabbit_channel 的消息,导致 rabbit_channel 很快耗光其 credit 值,也造成 flow。最终限流状态会一直追溯到最上层 connection。

此时客户端发送会卡顿(表现出等待confirm超时等异常)

因此,对于线上有业务量的RMQ集群,如果有大量消息堆积的队列需要清理,最好不要直接purge,有可能对线上业务造成影响

规避

最保险的清理方式,起 Consumers 消费(接收即丢弃)

附 rabbit_msg_store 进程有关的线上问题:

线上业务集群节点 TcpExt.pruneCalled 指标报警,同时发现有一队列处于flow状态,进而判断持久化进程 rabbit_msg_store 出现瓶颈,通过 sar 工具发现以下异常:

最终定位到节点对应的宿主机底层IO有问题,及时进行规避。

参考

Finding bottlenecks with RabbitMQ 3.3

Purge a large queue is slow

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。