RabbitMQ采坑记原创
RabbitMQ介绍
RabbitMQ是一个高可用和数据安全的消息管理系统。在业务中多应用实现业务间交互的异步解耦,通过MQ中间件实现业务的削峰填谷,保障业务避免高峰期流量激增冲击。简单说就是类似Kafka、RokectMQ消息中间件的存在,但相较其他消息中间件RabbitMQ提供了友好的后台管理和监控功能,毕竟是伴生功能,在更多的使用场景中还是会自建监控、告警等功能作为扩展,方便监控和预警。
事件描述
在业务的早期引入RabbitMQ作为消息中间件主要实现业务件的异步解耦,以及数据的安全性方面考虑,对于高峰期流量的处理没有强烈的要求,主要是业务发展的早期业务量很小。随业务的不断发展,业务量逐年的一个跳跃式的增长,RabbitMQ接受着一次又一次的考验。问题多发生在RabbitMQ在削峰填谷的处理机制–限流问题上,当消费者对于消息的消费能力不足时,RabbitMQ采取的信用证机制会逐层的传递至生产者侧,通过限制生产者的生产速率达到削峰的效果。个人的理解上不存在削峰也就不存在填谷的情况,毕竟消息并没有真正的发送到队列里缓存,预期消费者可以在业务低峰期去处理这些暂存的业务。
目前业务在用RabbitMQ的版本为3.7.9和3.9.8,较最新的3.12.x版本(实施升级时的最新版本为3.11.15)落后很多,在3.7.9到3.11.15版本间供修复了Bug108个,增强功能113个,安全漏洞1个,最主要的功能是增加了新的队列类型(quorum),新的同步机制(Raft)根据官方给出的数据显示较经典队列类型(Classic)提升40%左右的性能。鉴于提供的各种利好消息,促使我们进行RabbitMQ升级,在升级后在其中一套集群中发生了严重的限流,导致业务延时、消息丢失,严重是生产事故。
事件经过
鉴于RabbitMQ在新版本中带来的利好信息,促使我们有了升级的念头,于是开始着手调研、论证、测试验证等工作,在一系列的验证、讨论后决定由3.7.9版本升级到3.11.15,享受新版本(新队列类型、新同步机制)带来的利好。随这升级计划的出炉,安排了专人实施这项任务,在各集群升级的环节做了充分的风险评估以及应对策略。然而故障永远发生在没有意外的时候,上一秒还在讨论不出意外升级完最后一套集群就可以汇报了,于是意外就悄悄的降临了。只听急匆匆脚步声向我们这个区域赶来,人未至声音就已经传到了我们的耳朵里,集群的RabbitMQ管理后台打不开了,限流了。限流意味着业务延时、消息丢失等各种的业务风险,于是第一时间查看了监控并未出现告警,查看服务器资源监控,CPU已经达到了峰值(8C16G的虚拟机),判断是硬件资源不足导致业务高峰期支撑不住这业务量,紧急升级硬件资源到16C32G,在消息接管系统的加持下算是渡过了第一次的高峰期,但业务的延时是在所难免的。于是期望在晚高峰到来时能够撑过去,确保硬件资源足够不在出现限流问题。
在晚高峰到来时,还没等大家反应就出现了业务大面积的延时和限流现象。于是采用了多集群分流的方式将一半的流量拆分到另外的集群(生产者、消费者拆分一般服务器到另外的集群)。算是渡过了这个晚高峰。
事件分析
站在历史的角度看待这个问题发生的经过和影响,复盘整个事件能够让我们更好的了解真相、增长事件处理的经验。
- 验证阶段,通过分析技术验证的记录为单队列的功能验证、压测验证在8C16G的虚拟机上能够支撑过万的并发处理能力。相对于业务高峰期的4千并发是有很大的能力富余的。但对于个别业务的广播模式没有进行验证,而问题就出在业务的广播场景。
- 各位经验丰富的小伙伴也许已经看出来了,第一次高峰期到来时,为什么选择扩容操作,而不是回退到原来的版本就好了,是不是没有回退的方案?可以说是回退的方案是有的,但在事故发生后第一时间相信过往的经验可以解决此类问题,但严格按照规章制度操作更重要。
- 官方提供了那么多的利好信息,可在实际的业务场景中魔法失灵了。在Raft同步协议中,消息的发送、确认、消息的消费、确认命令都需要写入到一个WAL文件中,在队列分布不够均匀时,或者最坏的情况下(队列都在同一个节点上)这样就会导致这节点的出口流量非常的大,流量大的原因是消息的广播会按照订阅队列的数量复制,这样造成了消息量无形中被放大订阅队列的倍数。在Raft一致性协议的机制下向另外节点同步的流量同样会以消息倍数的关系进行放大同步。后来在一个千兆网卡的集群中模拟这个场景发现网卡的出口流量在3K消息大小2500/s的并发下就到了瓶颈(117MB)。
事件总结
- 严格按照规章制度、回退方案执行,降低业务故障持续的时长,确保业务的安全和稳定。
- 在评估官方提供的利好消息时一定要结合业务的实际场景。(脱离业务场景谈技术就是耍流氓)
- 新技术、新机制还是要充分的理解和掌握后再应用到生产环境,业务稳定放到第一位,对生产环境要有敬畏之心。