RocketMQ顺序消息消费重试DEMO

Producer - 加入了id为key,msg为bean的json字符

public class AddProducer {

    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("a-group");
        producer.setNamesrvAddr("192.168.0.211:9876");
        producer.start();

        Alarm alarm1 = new Alarm(1, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-01"));
        Alarm alarm2 = new Alarm(2, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-02"));
        Alarm alarm3 = new Alarm(3, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-03"));
        Alarm alarm4 = new Alarm(4, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-04"));
        Alarm alarm5 = new Alarm(5, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-05"));
        Alarm alarm6 = new Alarm(6, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-06"));
        Alarm alarm7 = new Alarm(7, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-07"));
        Alarm alarm8 = new Alarm(8, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-08"));
        Alarm alarm9 = new Alarm(9, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-09"));
        Alarm alarm10 = new Alarm(10, "add", new SimpleDateFormat("yyyy-MM-dd").parse("2024-01-10"));

        ArrayList<Alarm> list = new ArrayList<>();
        list.add(alarm1);
        list.add(alarm2);
        list.add(alarm3);
        list.add(alarm4);
        list.add(alarm5);
        list.add(alarm6);
        list.add(alarm7);
        list.add(alarm8);
        list.add(alarm9);
        list.add(alarm10);




        try {

            for (Alarm alarm : list) {
                Message msg = new Message("ALARM_RECORD", "add",String.valueOf(alarm.getId()), JSONUtil.toJsonStr(alarm).getBytes(RemotingHelper.DEFAULT_CHARSET));
                producer.send(msg,new CustomMessageQueueSelector(),alarm.getId(),new CustomSendCallback());
                System.out.println(alarm.getId() + " Continue execution ");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        // 6. 关闭生产者
//        producer.shutdown();
    }
}

Selector

public class CustomMessageQueueSelector implements MessageQueueSelector {
    @Override
    public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
        Integer id = (Integer) o;
        long index = id % list.size();
        return list.get((int) index);
    }
}

那么都知道 id 1和id9 用的是queue1

Consumer

public class ConsumerAdd {
 
    public static void main(String[] args) throws Exception {
        // 实例化消费者
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("record_add_group");
        // 指定Namesrv地址
        consumer.setNamesrvAddr("192.168.0.211:9876");
        // 订阅主题和标签
        consumer.subscribe("ALARM_RECORD", "add");
        // 设置Consumer第一次启动是从哪个位置开始消费
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
 
        // 注册消息监听器
        consumer.registerMessageListener(new CustomMessageListenerOrderly());
 
        // 启动消费者
        consumer.start();
        System.out.printf("Consumer tag add Started.%n");
    }
}

监听器

@Slf4j
public class CustomMessageListenerOrderly implements MessageListenerOrderly {

    /**
     * 保持消息消费的次数
     */
    Map<String, Integer> map  = new HashMap<>();

    @Override
    public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
        try {
            for (MessageExt msg : list) {
                String data = new String(msg.getBody());
                Alarm alarm = JSONUtil.toBean(data, Alarm.class);
                if(alarm.getId() == 1){
                    throw new Exception();
                }
                System.out.println("正常消费:" + alarm);
            }

            return ConsumeOrderlyStatus.SUCCESS;
        } catch (Exception e) {
            MessageExt msg = list.get(0);
            map.put(msg.getKeys(),msg.getReconsumeTimes()+1);
            log.info(msg.getKeys()+":"+map.get(msg.getKeys()));
            return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
        }

    }
}

id为1和9同queue,所以1报错,id9一定不会消费,那么返回SUSPEND_CURRENT_QUEUE_A_MOMENT   表示等一会,再继续处理这批消息

根据试验,确实如此,重试默认时间间隔可以看出是1S。默认次数Integer.MAX

当然可以通过

consumer.setSuspendCurrentQueueTimeMillis(2000);

修改重试时间间隔

也可以通过

consumer.setMaxReconsumeTimes(10);

修改顺序消费重试次数

再试一次

可以看到这条有问题的消息直接抛掉,消费ID9的了

重试topic也是没有的

那么去哪了呢?我们在死信队列中找到

总结:

如果业务需求对于顺序消费 - 强要求,那么推荐在监听器中根据map中一定要根据重试次数采取一定措施,通知到人。不然会造成消息堆积

如果顺序要求不高,或者不想这么麻烦,可以设置重试次数,使队列可以正常下去,做好记录及抛掉此消息后对后续消息影响的判断。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值