• RocketMQ的broker节点挂掉后重启导致的消息重复消费问题解决方案


    我的RocketMQ架构图如下:

    故障描述: Broker-b所在服务器宕机8小时(1:00-9:00), 重启Broker-b后, 8小时期间产生的消息被消费者订阅消费, 由于消费者等幂条件是:2小时内相同消息(msgId相同)不重复发送,  但是此时已经超过两小时, 故 1:00-7:00 期间产生的消息被重复消费。

    期望: Broker-b重启后, 消费者只订阅当前时间开始的消息, 之前的消息不再订阅。

    处理方案1:

    设置消费者订阅消息的位置为最新的位置

    consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);

    这里要注意代码注释。这个参数只对一个新的consumeGroup第一次启动时有效。
    就是说,如果是一个consumerGroup重启,他只会从自己上次消费到的offset,继续消费。这个参数是没用的。 而判断是不是一个新的ConsumerGroup是在broker端判断。
    要知道,消费到哪个offset最先是存在Consumer本地的,定时和broker同步自己的消费offset。broker在判断是不是一个新的consumergroup,就是查broker端有没有这个consumergroup的offset记录。

    另外,对于一个新的queue,这个参数也是没用的,都是从0开始消费。

    优点: 处理简单, 修改一行即可。

    缺点:需要新的消费者才生效, 故此方法对于broker中已包含了ConsumerGroup的, 不起作用。

    处理方案2:

    修改broker-b的offset, 和 broker-a的配置保持一致。

    方法:

    把broker-a的rocketmq路径:${userPath}/store/config/consumerOffset.json 文件拷贝到broker-b的相同位置, 重启broker-b

    优点: 简单快捷, 不需要修改程序代码。

    缺点:由于broker-a节点的消息队列一直在更新,offset偏移量一直在增加, 故复制过来到重启这段时间差内, 依然有少部分信息会被重复消费, 这只能依赖等幂代码的处理。

    事后补救:

     1. 消费者的等幂操作的条件时长设置更长(1天或者3天, 我是用redis存储msgId, 消息量不大的情况下可以设置更长的时长)

    //缓存消息ID 防止消费重复
    boolean setResult = RedisUtil.setnx(msgExt.getMsgId(), 60 * 60 * 24 * 1, msgExt.getMsgId().getBytes("utf-8"));
    if (!setResult) {
        // 存储不成功说明已经存储过, 直接返回成功
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }

    2. 增加rocketmq宕机告警

    总结:
    1. 程序等幂处理依然是关键, 把防重复寄望于MQ是不可靠的, 且MQ本来就是设计高并发, 这是它的优点, 所以还是在程序等幂处理上下功夫, 防止此类不可预知的问题导致数据重复。
    2. 监控很重要, 及时发现问题, 及时处理。
    3. 搞清楚原理, 减少误操作。
     
     
     
     
    参考链接:
  • 相关阅读:
    MySQL 一般模糊查询的几种用法
    MySQL插入中文数据报错
    BeanUtils.populate 的作用
    分分钟搞定 JSP 技术
    margin-top相对谁的问题
    常用汉字的Unicode码表
    从InputStream到String_写成函数
    Http请求和响应应用
    发布mvc报错:403.14-Forbidden Web 服务器被配置为不列出此目录的内容
    导出到excel
  • 原文地址:https://www.cnblogs.com/mhl1003/p/13954918.html
Copyright © 2020-2023  润新知