zoukankan      html  css  js  c++  java
  • 线上RocktMQ重复投递半事务消息故障排查

    1. 故障现象

        2020-11-18 10:40开始,业务线反馈线上收到大量的重复MQ半事务消息,导致容器资源消耗急剧攀升,经查看MQ日志,发现broker-b的Master服务,报出大量半事务消息回查日志,且每次回查的起始offset不变化,但opOffset不断迅速增大,且HALF_TOPIC队列急速膨胀,查看RocketMQ console监控web后台,发现出现大量消息堆积,且都在broker-B。offset日志如下:

    2. 原因分析

    2.1. MQ半事务消息回查机制

          1、producer提交半事务消息,会先存储在RMQ_SYS_TRANS_HALF_TOPIC队列(以下简称HALF队列)

          2、如果producer在MQ回查前,主动确认了本次事务结果,不管是提交还是回滚,MQ都会把该消息转存至RMQ_SYS_TRANS_OP_HALF_TOPIC队列(以下简称OP队列),且如果事务是成功提交的,同时把消息转存至真实的topic,让消费者进行消费。

          3、如果producer未能及时确认事务结果,则MQ会定时消费HALF队列,回查对应事务的结果,根据回查结果进行跟上述第二点一样的处理。

          4、如果当前事务消息尚未超时,则本次回查终止。

          5、更新HALF队列的消费进度offset,等待下一次定时回查,从最新进度offset开始。

    2.2. 故障原因

          1、MQ默认对超出4K的消息进行压缩存储,并设置sysFlag为已压缩

    2、半事务消息需要回查时,从HALF获取消息,如果消息是被压缩过的,此处会进行解压处理

    3、把解压后的消息renew一份,追加到HALF队列后,sysFlag标记仍为已压缩,但实际msgBody已经解压

    4、下次回查时,该事务消息仍未有结果,对其进行回查,但将其从HALF里读出时出现异常,因为根据sysFlag,消息进行了压缩,但实际进行解压又失败

    5、NPE异常未被捕获,直接抛到TransactionalMessageServiceImpl.check()方法,该方法仅打印出来,未作其他业务处理,导致未能更新HALF的回查offset,导致下次回查,仍然从上次的offset开始,陷入死循环

    3. 解决方案

    1、临时解决方案:

    • a)故障当天通过手动修改HALF的offset,让其跳过异常消息,得以恢复。
    • b)可以暂时修改transactionTimeout参数,加大半事务消息的事务超时时间,降低MQ回查的概率,规避出现故障。
    • c)可以暂时修改Producer端的compressMsgBodyOverHowmuch参数,加大启用压缩的阈值(不推荐)。         

     2、最终解决方案:

      MQ官方4.6.0+以后的版本已经修复该问题,对新版MQ进行稳定性测试后,升级到生产。

  • 相关阅读:
    JS站点
    1011 World Cup Betting (20分)
    1007 Maximum Subsequence Sum (25分)(动态规划DP)
    1006 Sign In and Sign Out (25分)
    1005 Spell It Right (20分)
    1004 Counting Leaves (30分)(DFS)
    1003 Emergency (25分)(Dijkstra算法)
    1002 A+B for Polynomials (25分)
    1001 A+B Format (20分)
    canvas
  • 原文地址:https://www.cnblogs.com/dennyzhangdd/p/14028635.html
Copyright © 2011-2022 走看看