SpringBoot整合RocketMQ发送顺序消息

简介: 严格按照消息的发送顺序进行消费的消息。默认情况下生产者会把消息以Round Robin轮询方式发送到不同的Queue分区队列,而消费消息时会从多个Queue上拉取消息,这种情况下的发送和消费是不能保证顺序的,如果将消息仅发送到同一个Queue中,消费时也只从这个Queue上拉取消息,就严格保证了消息的顺序性

1. 有序性分类
根据有序范围的不同,可以分为两种消息的有序性:分区有序和全局有序
分区有序
有多个Queue参与,其仅可保证在该Queue分区队列上的消息顺序,称为分区有序
在定义Producer时我们可以指定消息队列选择器,而这个选择器是我们自己实现了MessageQueueSelector接口定义的。在定义选择器的选择算法时,一般需要使用选择key。这个选择key可以是消息key也可以是其它数据。但无论谁做选择key,都不能重复,都是唯一的。

一般性的选择算法是,让选择key(或其hash值)与该Topic所包含的Queue的数量取模,其结果即为选择出的Queue的QueueId。

取模算法存在一个问题:不同选择key与Queue数量取模结果可能会是相同的,即不同选择key的消息可能会出现在相同的Queue,即同一个Consuemr可能会消费到不同选择key的消息。这个问题如何解决?一般性的作法是,从消息中获取到选择key,对其进行判断。若是当前Consumer需要消费的消息,则直接消费,否则,什么也不做。这种做法要求选择key要能够随着消息一起被Consumer获取到。此时使用消息key作为选择key是比较好的做法。
全局有序
当发送和消费参与的Queue只有一个时所保证的有序是整个Topic中消息的顺序,称为全局有序
在创建Topic时指定Queue的数量。有三种指定方式:

在代码中创建Producer时,可以指定其自动创建的Topic的Queue数量
在RocketMQ可视化控制台中手动创建Topic时指定Queue数量
使用mqadmin命令手动创建Topic时指定Queue数量
2. 生产者业务接口

public interface OrderMessageService {

    /**
     * 发送同步顺序消息
     * @param id
     * @param message
     */
    void sendSyncOrderMessage(String id, String message);

    /**
     * 发送异步顺序消息
     * @param id
     * @param message
     */
    void sendAsyncOrderMessage(String id, String message);

    /**
     * 发送单向顺序消息
     * @param id
     * @param message
     */
    void sendOnewayOrderMessage(String id, String message);
}

3. 生产者业务接口实现类


@Service
public class OrderMessageServiceImpl implements OrderMessageService {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    private static final Logger logger = LoggerFactory.getLogger(OrderMessageServiceImpl.class);

    @Override
    public void sendSyncOrderMessage(String id, String message) {
        Message<String> strMessage = MessageBuilder.withPayload(message).setHeader(RocketMQHeaders.KEYS, id).build();
        rocketMQTemplate.setMessageQueueSelector(new MessageQueueSelector() {
            @Override
            public MessageQueue select(List<MessageQueue> list, org.apache.rocketmq.common.message.Message message, Object obj) {
                Integer uid = Integer.valueOf(String.valueOf(obj));
                int index = uid % list.size();
                return list.get(index);
            }
        });
        SendResult result = rocketMQTemplate.syncSendOrderly("order-message-topic:sync-tags", strMessage, id);
        if (result.getSendStatus() == SendStatus.SEND_OK) {
            logger.info("发送同步顺序消息成功!");
        } else {
            logger.error("发送同步顺序消息失败!消息ID为:{}", result.getMsgId());
        }
    }

    @Override
    public void sendAsyncOrderMessage(String id, String message) {
        Message<String> strMessage = MessageBuilder.withPayload(message).setHeader(RocketMQHeaders.KEYS, id).build();
        rocketMQTemplate.setMessageQueueSelector(new MessageQueueSelector() {
            @Override
            public MessageQueue select(List<MessageQueue> list, org.apache.rocketmq.common.message.Message message, Object obj) {
                Integer uid = (Integer) obj;
                int index = uid % list.size();
                return list.get(index);
            }
        });
        rocketMQTemplate.asyncSendOrderly("order-message-topic:async-tags", strMessage, id, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                if (sendResult.getSendStatus() == SendStatus.SEND_OK) {
                    logger.info("发送异步顺序消息成功!消息ID为:{}", sendResult.getMsgId());
                }
            }
            @Override
            public void onException(Throwable throwable) {
                logger.info("发送异步顺序消息失败!失败原因为:{}", throwable.getMessage());
            }
        });
    }

    @Override
    public void sendOnewayOrderMessage(String id, String message) {
        Message<String> strMessage = MessageBuilder.withPayload(message).setHeader(RocketMQHeaders.KEYS, id).build();
        rocketMQTemplate.setMessageQueueSelector(new MessageQueueSelector() {
            @Override
            public MessageQueue select(List<MessageQueue> list, org.apache.rocketmq.common.message.Message message, Object obj) {
                Integer uid = (Integer) obj;
                int index = uid % list.size();
                return list.get(index);
            }
        });
        rocketMQTemplate.sendOneWayOrderly("order-message-topic:oneway-tags", strMessage, id);
    }
}

4. 消费者类

@Component
@RocketMQMessageListener(topic = "order-message-topic", consumerGroup = "order-consumer-group", consumeMode = ConsumeMode.ORDERLY)
public class OrderMessageListener implements RocketMQListener<String> {

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

    @Override
    public void onMessage(String message) {
        logger.info("接收到顺序消息为:{}", message);
    }
}

5. 测试

@Test
void orderMessage() {
    for (int i = 1; i < 5; i++) {
        orderMessageService.sendSyncOrderMessage(String.valueOf(i), "hello" + i);
    }
}
相关实践学习
RocketMQ一站式入门使用
从源码编译、部署broker、部署namesrv,使用java客户端首发消息等一站式入门RocketMQ。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
2月前
|
消息中间件 Java Maven
一文搞懂Spring Boot整合RocketMQ
一文搞懂Spring Boot整合RocketMQ
98 0
|
消息中间件 Java RocketMQ
SpringBoot整合RocketMQ发送批量消息
SpringBoot整合RocketMQ发送批量消息
|
6月前
|
消息中间件 Java RocketMQ
低版本的springboot(1.5.3)如何集成rocketmq
低版本的springboot(1.5.3)如何集成rocketmq
154 1
|
3月前
|
消息中间件 数据库 RocketMQ
Springboot+RocketMQ通过事务消息优雅的实现订单支付功能
RocketMQ的事务消息,是指发送消息事件和其他事件需要同时成功或同时失败。比如银行转账, A银行的某账户要转一万元到B银行的某账户。A银行发送“B银行账户增加一万元”这个消息,要和“从A银 行账户扣除一万元”这个操作同时成功或者同时失败。RocketMQ采用两阶段提交的方式实现事务消息。
115 0
|
3月前
|
消息中间件 Java RocketMQ
Springboot整合RocketMQ 基本消息处理
1. 同步消息 2. 异步消息 3. 单向消息 4. 延迟消息 5. 批量消息 6. 顺序消息 7. Tag过滤 8. 广播消息
|
8月前
|
消息中间件 Java 程序员
SpringBoot整合RocketMQ,尝尝几大高级特性!
作为一名程序员,您一定熟悉RocketMQ的功能,包括支持事务、顺序和延迟消息等。在程序员界有一句名言,“Talk is cheap. Show me the code” 。本文将通过实际案例来引出解决方案,并通过代码实现,让您在学习本节的过程中能够确切地掌握实际编码技能
144 0
SpringBoot整合RocketMQ,尝尝几大高级特性!
|
8月前
|
消息中间件 物联网 大数据
SpringBoot3集成RocketMq
RocketMQ因其架构简单、业务功能丰富、具备极强可扩展性等特点被广泛应用,比如金融业务、互联网、大数据、物联网等领域的业务场景;
238 0
|
9月前
|
消息中间件 Java API
RocketMQ极简入门-在SpringBoot中使用RocketMQ
现在开发项目都是基于SpringBoot,新项目很少使用Spring,所以我们学习一门技术除了要会原生API,还不得不考虑和SpringBoot集成,本篇文章为SpirngBoot整合RocketMQ案例
402 0
|
9月前
|
消息中间件 Java API
八.RocketMQ极简入门-在SpringBoot中使用RocketMQ
RocketMQ极简入门-在SpringBoot中使用RocketMQ
|
9月前
|
Java
SpringBoot整合RocketMQ
SpringBoot整合RocketMQ
303 0