RocketMq顺序消费

如果要保证顺序消费,那么他的核心点就是:生产者有序存储、消费者有序消费

实现原理

这里有一点很重要的是:同一个queue,存储在里面的message 是按照先进先出的原则

OK,我们继续接着之前的demo springboot集成RocketMq

  1. 生产端:我们知道生产的message最终会存放在Queue中,生产者发送消息的思路就是:在一个订单操作的过程中,订单产生,订单支付,订单生成,产生的这四个消息,都发送到同一个Queue队列中,那么取消息的时候就可以保证先进先取出。
    在这里插入图片描述试想,如果要是不发送同一个Queue中,发送到一个topic下的所有队列中,能保证顺序吗?肯定不能,因为在一个消费者集群的情况下,消费者1先去Queue拿消息,它拿到了 订单生成,它拿完后,消费者2去queue拿到的是 订单支付。拿的顺序是没毛病了,但关键是先拿到不代表先消费完它。会存在虽然你消费者1先拿到订单生成,但由于网络等原因,消费者2比你真正的先消费消息。这是不是很尴尬了。

  2. 消费端:我们采用锁,锁住一个Queue,当线程1来取订单消息的时候,取到订单完成,然后给这个队列加一把锁,然后等线程1消费完订单完成这个消息之后,在去取下一个消息订单支付。这样的一个过程来真正保证对同一个Queue能够真正意义上的顺序消费,而不仅仅是顺序取出。

注意事项:
1、顺序消息暂不支持广播模式。
2、顺序消息不支持异步发送方式,否则将无法严格保证顺序。
3、建议同一个 Group ID 只对应一种类型的 Topic,即不同时用于顺序消息和无序消息的收发。
4、对于全局顺序消息,建议创建实例个数 >=2。

代码实现

  • 生产端 同一orderID的订单放到同一个queue。
  • 消费端 同一个queue取出消息的时候锁住整个queue,直到消费后再解锁。

MQ消息投递机制,我们可以用SelectMessageQueueByHash算法

代码实现:

@RequestMapping("/test/sendRocketMq")
    @ResponseBody
    public String sendRocketMq(){
        List<Order> orderNoList = new ArrayList<Order>();
        orderNoList.add(new Order("DD0000003","订单创建"));
        orderNoList.add(new Order("DD0000003","订单支付"));
        orderNoList.add(new Order("DD0000004","订单创建"));
        orderNoList.add(new Order("DD0000003","订单完成"));
        orderNoList.add(new Order("DD0000005","订单支付"));
        orderNoList.add(new Order("DD0000006","订单创建"));
        orderNoList.add(new Order("DD0000004","订单支付"));
        orderNoList.add(new Order("DD0000005","订单创建"));
        orderNoList.add(new Order("DD0000006","订单支付"));
        orderNoList.add(new Order("DD0000006","订单完成"));
        orderNoList.add(new Order("DD0000005","订单完成"));
        orderNoList.add(new Order("DD0000004","订单完成"));
        for (int i = 0; i < orderNoList.size(); i++) {
            SendResult sendResult = sendMsgUtil.sendMsg(orderNoList.get(i), MsgProductBean.ACCOUNT_INFO);
        }
        return "success";
    }

MQ工具类修改:

public SendResult sendMsg(Order order, MsgProductBean object) {
        Message message = new Message(topic, object.getTagName(), (order.getOrderNo() + " : " + order.getOrderStatus()).getBytes());
        try {
//            SendResult sendResult = defaultMQProducer.send(message);

            SendResult sendResult = defaultMQProducer.send(message, new SelectMessageQueueByHash(), order.getOrderNo());

            logger.info("Product:发送状态={}, 存储queue={}, 订单order={} , 订单状态status={}", sendResult.getSendStatus(),
                    sendResult.getMessageQueue().getQueueId(), order.getOrderNo(), order.getOrderStatus());
            return null;
        } catch (MQClientException e) {
            e.printStackTrace();
        } catch (RemotingException e) {
            e.printStackTrace();
        } catch (MQBrokerException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

结果:
在这里插入图片描述
接下里我们来说消费端:
这里主要用到的是这个类:MessageListenerOrderly

@Component
//public class MsgListener implements MessageListenerConcurrently {
public class MsgListener implements MessageListenerOrderly {

    final static Logger logger = LoggerFactory.getLogger(MsgListener.class);

    @Autowired
    private MsgCustomerService msgCustomerService;

    int i = 0;

    @Override
    public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
        msgs.forEach(messageExt -> {
            msgCustomerService.handlerMsg(messageExt);
        });
        i ++ ;
        logger.info("i = " + i);
        return ConsumeOrderlyStatus.SUCCESS;
    }

//    @Override
//    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
//        msgs.forEach(messageExt -> {
//            msgCustomerService.handlerMsg(messageExt);
//        });
//        i ++ ;
//        logger.info("i = " + i);
//        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
//    }
}

结果如下:
在这里插入图片描述

OK,这样就实现了我们的顺序消费。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值