ArtemisMQ的“未消费之谜”
2018年6月份,我们开发了两个使用Artemis做消息队列实现的积分模块和PUSH推送模块,在几轮测试以后,大家信心满满的正式上线了,而且经过了一个多月使用,一切都很顺利,感觉生活一切都美美的。
问题来了
2018年8月份,突然有一天前面传来噩耗,用户注册后没收到积分,这真是迎头一棒啊。但是,我不能因为一次打击就失去对Artemis的信任,于是对整个模块进行了代码分析,结果发现代码没问题,妥妥的!
分析问题
查看Artemis控制台,发现有很多未消费的消息,之前一个多月都没有问题,都未出现过未消费的消息,就中间做过一次升级上线。
通过仔细慎重的分析所有的证据,我断定这是一次重启引发的“血案”!
如果在某一个Artemis节点上有很多未消费的消息,而且还在增多,那么只有一个可能,这个节点上没有consumer连接,而且这个节点上的消息不能redistribute到其他节点上,既然这样问题就很清楚了。这个节点上没有Consumer连接为什么producer还一直发送消息呢?
正常情况下有Consumer才会把消息发送到该节点上的。这在测试环境上是不存在的,而且没有consumer有消息过来正常情况也应该redistribute到其他节点的,所以
我推测是Artemis的集群出了问题了,而且查看Artemis生产环境下链接到61616端口的链接TIME_WAIT的较多。
于是我做了以下两种调整:
修改linux网络配置
修改linux的网络配置,减少TIME_WAIT连接,减少断开的识别时间。具体操作步骤如下:
打开文件 /etc/sysctl.conf,编辑文件,加入以下内容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
然后执行 /sbin/sysctl -p 让参数生效。
修改Artemis集群方式
我把Artemis的集群由UDP改为了static集群方式。
通过以上修改保证了客户端连接能够快速的断开,在应用重启时不会持续往这边发送消息,我使用jmeter进行压测,重启消费者过程中,消息redistribute都正常。
SpringJms的坑
这就完美了吗?NO!又发现新问题了。
在50个线程压测时进行重启应用,虽然重启后消息消费和redistribute正常,但是在重启的那一瞬间,在使用ON_DEMAND模式下节点上消费者断开的一瞬间服务器判断有一部分延迟,还是有一部分的消息发送到了没有consumer的节点上,这些消费者不能再被redistribute,这可能是Artemis的一个bug。
怎么办呢?为什么应用只能连接到一个节点上呢?这也不能说是spring-jms的一个坑,还是对spring-jms不够数量,spring-jms在创建消费监听的时候,无论有多少个Session,都只会创建一个共享连接,无论你有多少个Artemis节点,一个应用就永远只会连到一个节点,这真是大大的浪费呀。这个真是SpringJms的坑。
自己动手,丰衣足食
难道Artemis真的就这么差吗?实际上我看了Artemis自带的客户端以后,发现其实它在创建连接时自带三种策略,
一种是轮询,这种适合性能要求比较高的场景,提高消费效率的。
一种是随机,随便选一个节点连上就可以了,不知道为什么有这种策略。
一种是只取第一个节点,这种适合做双机热备的场景。
因此这个SpringJms带的坑,还得自己填,使用自带client进行创建消费者监听
这样的情况下,只要最大连接数超过2个以上,通过轮询的方式创建连接,就会平均创建到多个节点上,即使重启过程中有几个消息不能redistribute重启以后有消费者连接上来就能继续消费。
好吧,大功告成,生活终于又美好了。
———— / END / ———-