消息队列 RocketMQ:(四)顺序消息

文章目录

消息队列 RocketMQ:(一)概述

消息队列 RocketMQ:(二)系统架构

消息队列 RocketMQ:(三)发送普通消息(三种方式)


前提

创建一个 Maven 的 Java 工程。
引入 RocketMQ 的 Client 依赖,版本需要和服务端一致。

	<dependency>
	    <groupId>org.apache.rocketmq</groupId>
	    <artifactId>rocketmq-client</artifactId>
	    <version>4.9.0</version>
	</dependency>

二、顺序消息

顺序消息(FIFO 消息),顺序消息指的是,严格按照消息的 发送顺序 进行消费的消息。

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

1、有序性分类

根据有序范围的不同,RocketMQ 可以严格地保证两种消息的有序性:分区有序全局有序

  • 全局顺序:对于指定的一个 Topic,所有消息按照严格的先入先出 FIFO(First In First Out)的顺序进行发布和消费。
  • 分区顺序:对于指定的一个 Topic,所有消息根据 Sharding Key 进行区块分区。同一个分区内的消息按照严格的 FIFO 顺序进行发布和消费。Sharding Key 是顺序消息中用来区分不同分区的关键字段,和普通消息的 Key 是完全不同的概念。

2、全局有序

当发送和消费参与的 Queue 只有一个时所保证的有序是整个 Topic 中消息的顺序,称为全局有序

在这里插入图片描述

在创建 Topic 时指定 Queue 的数量。有三种指定方式:

  1. 在代码中创建 Producer 时,可以指定其自动创建的 Topic 的 Queue 数量
  2. 在 RocketMQ 可视化控制台中手动创建 Topic 时指定 Queue 数量
  3. 使用 mqadmin 命令手动创建 Topic 时指定 Queue 数量

3、分区有序

如果有多个 Queue 参与,其仅可保证在该 Queue 分区队列上的消息顺序,则称为分区有序

在这里插入图片描述

在定义 Producer 时我们可以指定消息队列选择器,而这个选择器是我们自己实现了 MessageQueueSelector 接口定义的。

在定义选择器的选择算法时,一般需要使用选择 key。这个选择 key 可以是消息 key 也可以是其它数据。但无论谁做选择 key,都不能重复,都是唯一的。

4、发送顺序消息

全局顺序消息和分区顺序消息的发布方式基本一样

package com.learn.rocketmq.order;

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.common.RemotingHelper;

import java.util.List;

public class OrderedProducer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("pg");
        producer.setNamesrvAddr("localhost:9876");
        // 若为全局有序,则需要设置Queue数量为1
        // producer.setDefaultTopicQueueNums(1);

        producer.start();

        for (int i = 0; i < 100; i++) {
            // 演示简单,使用整型数作为orderId
            Integer orderId = i;
            byte[] body = ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET);
            Message msg = new Message("LearnTopic", "DonTag-Ordered", body);
            // 将orderId作为消息key
            msg.setKeys(orderId.toString());
            // send()的第三个参数值会传递给选择器的select()的第三个参数
            // 该send()为同步发送
            SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
                // 具体的选择算法在该方法中定义
                @Override
                public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                    // 以下是使用消息key作为选择的选择算法
                    String keys = msg.getKeys();
                    Integer id = Integer.valueOf(keys);

                    // 以下是使用arg作为选择key的选择算法
                    // Integer id = (Integer) arg;

                    int index = id % mqs.size();
                    return mqs.get(index);
                }
            }, orderId);

            System.out.println(sendResult);
        }
        producer.shutdown();
    }
}

5、订阅顺序消息

全局顺序消息和分区顺序消息的订阅方式基本一样

package com.learn.rocketmq.order;

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.*;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;

import java.util.List;

public class OrderedConsumer {

    public static void main(String[] args) throws MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("cg");
        consumer.setNamesrvAddr("localhost:9876");
        // 指定从第一条消息开始消费
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        // 指定消费topic与tag
        consumer.subscribe("LearnTopic", "*");

        // 注册消息监听器
        consumer.registerMessageListener(new MessageListenerOrderly() {
            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
                // 逐条消费消息
                for (MessageExt msg : list) {
                    System.out.println(msg);
                }
                // 返回消费状态:消费成功
                return ConsumeOrderlyStatus.SUCCESS;
            }
        });

        // 开启消费者消费
        consumer.start();
        System.out.println("Consumer Started");
    }
}

RocketMQ Order Message

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值