RocketMQ系统性学习-SpringCloud Alibaba集成RocketMQ以及顺序消费实战

顺序消费实战

顺序消费分为两种:

  • 全局有序:适用于并发度不大,并且对消息要求严格一致性的场景下

    通过创建一个 topic,并且该 topic 下只有一个队列,那么生产者向着一个队列中发消息,消费者也在这一个队列中消费消息,来保证消息的有序性

  • 局部有序:适用于对性能要求比较高的场景,在设计层面将需要保证有序的消息放在 Topic 下的同一个队列即可保证有序

全局有序

要保证全局有序的话,我们先通过上边启动的 Dashboard 项目,创建一个只有一个队列的 Topic

写队列和读队列 都设置为 1 个,perm 设置为6(perm,2:只写; 4-只读; 6-读写;)

全局有序流程图如下:

首先消费者主启动类如下:

java 复制代码
@SpringBootApplication
@EnableBinding({CustomSink.class })
public class OrderlyConsumerApplication {

    @Value("${server.port}")
    private int port;

    public static void main(String[] args) {
        SpringApplication.run(OrderlyConsumerApplication.class, args);
        System.out.println("【【【【【  OrderlyConsumerApplication 启动成功!!!   】】】】】");
    }

    // 定义两个通道,input 接收全局有序消息,input2 接收局部有序消息
    @StreamListener("input")
    public void receiveInput(String receiveMsg) {
        System.out.println(port + " port, input receive: " + receiveMsg);
    }

    @StreamListener("input2")
    public void receiveInput2(String receiveMsg) {
        System.out.println(port + " port, input2 receive: " + receiveMsg);
    }
}

自定义 CustomSink 如下:

java 复制代码
public interface CustomSink extends Sink {

    /**
     * Input channel name.
     */
    String INPUT2 = "input2";

    /**
     * @return input channel.
     */
    @Input(CustomSink.INPUT2)
    SubscribableChannel input2();
}

配置类 application.properties 如下:

properties 复制代码
spring.application.name=mq_orderly_consumer
server.port=9530

# configure the nameserver of rocketmq
spring.cloud.stream.rocketmq.binder.name-server=127.0.0.1:9876
spring.cloud.stream.rocketmq.binder.group=mq_producer_group

# configure the input binding named input
spring.cloud.stream.bindings.input.destination=Global-Orderly-Topic
spring.cloud.stream.bindings.input.content-type=application/json
spring.cloud.stream.bindings.input.group=Global-Orderly-Topic-group
spring.cloud.stream.rocketmq.bindings.input.consumer.orderly=true

# configure the input binding named input
spring.cloud.stream.bindings.input2.destination=Partly-Orderly-Topic
spring.cloud.stream.bindings.input2.content-type=application/json
spring.cloud.stream.bindings.input2.group=Partly-Orderly-Topic-group
spring.cloud.stream.rocketmq.bindings.input2.consumer.orderly=true

全局有序生产者代码如下:

java 复制代码
public class GlobalProducer {

    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer(
                "producer_group",
                true);
        producer.setNamesrvAddr("218.95.37.160:9876");
        producer.start();

        for (int i = 0; i < 12; i++) {
            Message msg = new Message(
                    "Global-Orderly-Topic",
                    "Global_Orderly_Tag",
                    ("( " + i + " )message from GlobalProducer").getBytes());
            msg.setKeys("Global_Orderly_Tag");
            producer.send(msg);
        }
        System.out.println("Send Finished.");
    }
}

先启动消费者,再启动生产者,即可看到在消费者端,消息被有序消费

局部有序

局部有序的话,我们将需要保证有序的消息放在同一个 Topic 下的队列即可保证有序,这里设计的让 OrderId 相同的消息放在同一个队列中发送,流程图如下:

在局部有序中,消费者依然使用全局有序中的消费者,局部生产者代码如下:

java 复制代码
public class PartlyProducer {

    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer(
                "producer_group",
                true);
        producer.setNamesrvAddr("127.0.0.1:9876");
        producer.start();

        /**
         * orderId = 1 的消息,需要按照 step 的顺序进行消费
         * orderId = 2 的消息,需要按照 step 的顺序进行消费
         */
        List<Order> list = new ArrayList<>();
        for (int i = 1; i <= 3; i ++) {
            Order order = new Order();
            order.orderId = 1;
            order.step = i;
            list.add(order);
        }
        for (int i = 5; i <= 8; i ++) {
            Order order = new Order();
            order.orderId = 2;
            order.step = i;
            list.add(order);
        }

        System.out.println(list);

        int size = list.size();
        for (int i = 0; i < size; i++) {
            Order order = list.get(i);
            Message msg = new Message(
                    "Partly-Orderly-Topic",
                    "Partly_Orderly_Tag",
                    (order.toString()).getBytes());
            msg.setKeys("Partly_Orderly_Tag");
            /**
             * 这里发送消息的时候,根据 orderId 来选择对应发送的队列
             */
            producer.send(msg, new MessageQueueSelector() {
                @Override
                public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                    int orderId = (int)arg;
                    int idx = orderId % mqs.size();
                    return mqs.get(idx);
                }
            }, order.orderId);
        }
        System.out.println("Send Finished.");
    }



    public static class Order {
        int orderId;
        int step;
        @Override
        public String toString() {
            return "Order{" +
                    "orderId=" + orderId +
                    ", step=" + step +
                    '}';
        }
    }
}
相关推荐
爱吃苹果的日记本1 小时前
数据结构第三课(时间复杂度)
数据结构·学习
泡泡鱼(敲代码中)4 小时前
MySQL基础学习笔记:从数据模型到DDL全掌握
开发语言·数据库·笔记·学习·mysql
fanged5 小时前
Agent的Skills(TODO)
学习
是隼人5 小时前
buuctf-pwn picoctf_2018_shellcode(ret2shellcode)题解(学习过程持续更新)
c语言·学习·安全·pwn入门·ctf入门
m4Rk_6 小时前
【论文阅读】Agent 记忆机制(69):STITCH——用上下文意图解决“语义相关但情境错误”的记忆检索
论文阅读·人工智能·学习·开源·github
2601_949950636 小时前
个人在线刷题的工具
学习·考研·小程序·刷题·小程序推荐
一尘之中6 小时前
深入解析面向服务的架构(SOA):从特性到实施
学习·架构·ai写作
秦哈哈8 小时前
【Hello Agents】学习笔记(二)
笔记·学习·microsoft
sunshine22 girl8 小时前
Java学习三 高级语法3, 方法
java·学习
传奇开心果编程9 小时前
【dioxus0.7基础语法学与练】第8课:RSX 宏 — Dioxus 声明式 UI 语法
开发语言·学习·rust