计算机系统应用教程网站

网站首页 > 技术文章 正文

一次生产环境RabbitMQ故障定位

btikc 2025-02-07 16:40:14 技术文章 13 ℃ 0 评论

一天公司运维跑过来跟我说,前两天生产环境rabbitmq镜像集群节点都莫名重启了,让我看看是咋回事。我一愣,rabbitmq节点还会莫名重启,我还是第一次听说呢。此时的我毫无头绪,但是要想做一名技术大佬怎么能说不会呢,于是我淡定的跟他说:"小意思,我去瞅瞅"。


首先我第一个想到的就是去查看RabbitMQ的崩溃日志,erl_crash.dump这个文件,RabbitMQ在发生异常退出的时候都会将崩溃原因记录在这个文件里。


但是坑爹的是,因为这次事故是好几天前发生的了,而且前两天机房网络切割又把RabbitMQ进程重启了,所以当时的事故现场已经被冲掉了。


没办法了,于是我找运维要了当时的rabbitmq运行日志,打开日志文件发现了这么一个东西:

说明当时的mq生产者已经被阻塞了无法在进行消息的投递。这里需要讲到rabbitmq部署时的一个参数:vm_memory_high_watermark,它的默认值是0.4,意思就是当RabbitMQ服务器使用40%以上的可用RAM时,它将发出内存警报并阻止所有正在发布消息的连接。一旦清除了内存警报(例如,由于服务器将消息分页到磁盘或将其传递到使用并确认传递的客户端),便会恢复正常服务。


但是,请注意,这不会阻止RabbitMQ服务器使用超过40%的资源,这仅仅是限制发布者的点。在最坏的情况下,Erlang的垃圾收集器可能导致使用的内存量增加一倍(默认情况下,RAM的80%)


好,我们知道此时rabbitmq内部触发了流控,直到RAM中的数据刷盘或被消费之后生产者才能继续投递消息。但是这似乎跟节点重启没什么关系呀,我已经触发了流控正常来说就不可能把服务器内存打满导致节点挂掉的。猜测没啥用,我们直接动手模拟下这个情况,看看此时rabbitmq进程会不会重启。


内存压力测试

让我们准备三台虚拟机,机器配置:单核内存1G

  • 192.168.1.103(node01)
  • 192.168.1.104(node02)
  • 192.168.1.105(node03)

rabbitmq环境搭建,这里跟生产环境选择版本一致:

erlang:erlang-19.3.6.4-1.el7.centos.x86_64.rpm

rabbitmq:rabbitmq-server-3.7.4-1.el6.noarch.rpm


镜像集群设置:

rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'


启动各个节点的rabbitmq服务,环境验证:

让我们开始进行写入测试,创建测试队列test-queue:

为了方便快速模拟内存积压场景,所以我们将队列消息设置为不持久化,然后开始多线程并发写入:

此时观察rabbitmq内部日志以及机器内存使用情况,发现很快内存占用达到阈值,触发rabbitmq内部流控:

此时去查看rabbitmq进程,一切正常并未出现进程自动重启的情况。


一定是其他的原因,于是我重新翻阅了下官方文档,然而没有找到任何关于节点自动重启的相关信息。

但是,在群集和网络分区那一栏中却有了新的发现,RabbitMQ中总共提供了四种处理网络分区的方式:

  • ignore(默认模式,忽略网络分区)
  • pause-minority(暂停少数模式)
  • pause-if-all-down(全屏暂停模式)
  • autoheal(自动修复模式)


其中pause-minority、pause-if-all-down、autoheal模式下都可能会暂停部分节点,直到网络分区结束再次启动。难道是当时出现了网络分区导致了全部节点重启?让我们再次动手实践下。


分区测试

ignore

1、模拟网络分区出现,在node01节点上执行:

iptables -A INPUT -s 192.168.1.104 -j DROP;iptables -A INPUT -s 192.168.1.105 -j DROP

2、此时观察集群状态,发现此时集群并未检测分区发生,进程状态正常:

3、在逐步模拟另两台节点分区:

在node02节点上执行:

iptables -A INPUT -s 192.168.1.103 -j DROP;iptables -A INPUT -s 192.168.1.105 -j DROP

在node03节点上执行:

iptables -A INPUT -s 192.168.1.103 -j DROP;iptables -A INPUT -s 192.168.1.104 -j DROP

模拟三台节点之间全部出现网络分区后,观察发现进程状态依然正常:

结论:ignore模式下集群会忽略网络分区,节点之间会独立运行。


pause-minority

1、修改
/etc/rabbitmq/rabbitmq.conf配置文件,将
cluster_partition_handling设置为pause_minority,然后重新启动集群。

2、模拟网络分区出现,在node01节点上执行:

iptables -A INPUT -s 192.168.1.104 -j DROP;iptables -A INPUT -s 192.168.1.105 -j DROP

3、此时观察集群状态,发现问题产生,node01节点上rabbitmq app主服务被关闭了,此时进程并未重启。再观察另外两个节点,发现另两个节点检测到了分区发生,并且进程状态正常。

4、继续模拟另两个节点分区,在node02节点上执行:

iptables -A INPUT -s 192.168.1.103 -j DROP;iptables -A INPUT -s 192.168.1.105 -j DROP

此时观察集群状态,发现分区发生后,node02节点出现了跟上面一样的情况,主进程app停止服务,rabbitmq节点不可用。再检查第三个节点,发现第三个节点也一样停止了服务

5、让我们来逐步恢复网络,在各个节点下执行:iptables -F

此时观察各个节点状态,发现各节点服务发生了重启!!!

结论:在pause-minority模式下,当网络分区发生时,各个节点判定自己处于少数(即少于或等于节点总数的一半)的时候,会暂停当前节点的服务,直到网络分区恢复时会再次启动。


pause-if-all-down

1、修改各个节点的
/etc/rabbitmq/rabbitmq.conf配置文件为如下,然后重启服务

2、模拟分区发生,在node01节点下执行:

iptables -A INPUT -s 192.168.1.104 -j DROP;iptables -A INPUT -s 192.168.1.105 -j DROP

3、此时观察集群状态,发现主APP进程已关闭,节点变成不可用:

4、再继续模拟节点2的分区:

此时观察服务器状态,发现此时节点2和节点3上的主app进程也都被关闭了,此时集群全部节点主服务都已停止:

5、尝试恢复网络,发现全部节点都发生了重启:

结论:在pause-if-all-down模式下,节点如果和配置的list节点都无法通信时,此时该节点会自动关闭,等到网络分区恢复后会自动重新启动。


autoheal

1、修改
/etc/rabbitmq/rabbitmq.conf配置文件,将
cluster_partition_handling设置为autoheal,然后重新启动集群。

2、模拟网络分区出现:

3、此时观察各节点运行情况,发现此时三个节点都运行正常:

4、继续模拟其他节点的网络分区,发现此时各个节点各自运行正常:

此时我们再去看看生产环境的配置,发现我们刚好就是用的pause_minority模式,我们刚刚在pause_minority的测试中确实出现了跟生产环境一样的情况。

但是因为第一现场没有保留到嘛,所以我们也不能100%下结论。基于上面测试我们可以猜测故障当晚的情况应该是这样的:

有两个节点出现了网络分区,然后节点因为数据没法复制到其他节点积压在内存中导致了内存飙升。随后集群检测到分区发生,关闭了全部节点,随后网络恢复后全部节点自动重启。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表