测试报 bug 说,使用 auto-delete queue 时出现了奇怪的问题:
由于这个问题出现了较长时间,且一直没有定论,于是我决定出手一探究竟
解决问题的关键,其实是能够复现出问题;
由于脑裂问题涉及到多节点通信,理论上至少需要 2 或 3 个机器才行,此时采用 docker 的来处理该问题,最为合适;
复现步骤简单如下:
基于 C 客户端测试
基于 docker-compose 构建 2 node rabbitmq cluster
基于配置,默认在 rabbit1 节点上创建名为 auto-delete.queue 的 AD queue
{
"name": "auto-delete.queue",
"vhost": "/",
"durable": false,
"auto_delete": true,
"arguments": {}
}
cluster 运行信息如下
基于配置创建出来的 AD queue ,初始状态如下
运行测试客户端,创建一个消费者到 AD queue 上
rabbit1 收到连接信息
将 rabbit1 从 docker network 中踢出,模拟脑裂情况
从 rabbit1 和 rabbit2 的管理界面上很快会发现“卡住”状态的发生,之后点击页面元素无法正常响应
服务器侧:rabbit1 发现心跳超时(这里我们采用的心跳时间为5秒),故主动关闭了客户端 tcp 连接,之后 rabbit2 上立刻收到了来自客户端的重连
客户端侧:在 net tick 超时前,协议流程阻塞在接收 Queue.Declare-Ok 上
大约 1min 后(net tick 超时),rabbit2 的管理界面恢复可用状态,此时可以看到,rabbit2 判定 rabbit1 已经下线
服务器侧:rabbit1 和 rabbit2 均认为对端已下线,原因为 net_tick_timeout
客户端侧:在 net tick 超时后,在 rabbit2 上的协议流程将会继续完成剩余部分
此时,管理界面可以确认上述消费者的情况
同时,可以测试下,在 rabbit2 上新建的 AD queue 以及消费者能否正常工作
可以看到,工作完全正常
将 rabbit1 重新加入 docker network 中,模拟脑裂恢复
服务器侧:
客户端侧:保持之前的状态,没有任何变化
由于之前 bug 是打在 python 客户端上,若不基于 pika 也测试一次恐难服众,于是
基于 pika 创建一个消费者
正常完成协议流程
将 rabbit1 从 docker network 中踢掉,管理页面开始进入不可用状态
服务器侧:tcp 连接从 rabbit1 上断开,重新向 rabbit2 建立
客户端侧:在 net tick 超时前,协议流程同样阻塞在接收 Queue.Declare-Ok 上(这里我加的打印不太好)
net tick 超时后,管理页面恢复可用状态
服务器侧:rabbit1 和 rabbit2 相互认为对端已经掉线
客户端侧:继续完成剩下的协议流程
最终效果,pika 客户端成功在 rabbit2 上创建了 AD queue ,完成了 bind 和 consume 操作
完整过程的抓包如下
之后,恢复 rabbit1 的网络,行为和之前的实验一致,不再赘述
由于报 bug 的环境使用的是 RabbitMQ 3.7.17 古董版本,而我用于复现的版本是最新的 3.9.8,害怕被人说结果不符合要求,于是又屁颠屁颠的基于 3.7.17 重新测试了一遍,结果没差别,不再赘述。
上述测试中,建立最初的消费关系时,客户端均是连接的 rabbit1 节点,没有测试创建 AD queue 在 rabbit1 上,但客户端却连到 rabbit2 上完成 bind + comsume 的情况(这种情况,其实并不符合常规逻辑)
测试的 bug 说,之前出现问题时似乎是阻塞在了 queue bind 上,而不是 queue declare 上,但这种情况在我的复现中始终无法出现
无论是基于古董版本 3.7.17 ,还是最新版本 3.9.8 ,针对上述问题,行为是一致的
无论是采用我们自己实现的 C 库客户端,还是基于 pika 的客户端,针对上述问题,行为是一致的
当支持自动切换逻辑的客户端遇到连接节点直接掉线的情况时,会触发连接到新节点的逻辑,会成功建立 TCP 连接,以及 AMQP 层面的 Channel ,之后发出 Queue.Declare 后,会阻塞在接收 Queue.Declare-Ok 上,阻塞时间长度取决于 net_tick_timeout 的值,阻塞期间 AMQP 协议层面的 heartbeat 能够正常交互;当net_tick_timeout 时间到达后,协议流程将自动恢复,之后客户端会在 rabbit2 上创建完成相应的 queue、bind 关系,以及 consume 成功;
由于未能复现出 bug 中所说的“绑定关系丢失”,所以这篇文章并未说解决了问题,而是提供一个基础测试数据,在此基础上,如果再次出现上述问题,应该可以做的更好一点
“竞态”问题是否真的存在:rabbitmq 底层对于“脑裂”的处理,与上层业务会重连后的动作,是否真的在“元信息”处理上存在“竞态”关系?
auto-delete queue 是否存在“无解”的使用问题,是否可以和其他属性搭配使用会有更好的效果?
(如果有时间,后面我再补一篇基于源码的讨论)
PS:本文敏感字如下,呵呵
原创不易,添加关注,点赞,在看,分享是最好的支持, 谢谢~
更多精彩内容,欢迎关注微信公众号 西风冷楼阙
|