# 全面解剖 消息中间件 RocketMQ-(4)

全面解剖 消息中间件 RocketMQ-(4)

一、RocketMQ 顺序消息分析

1、消息有序:指的是可以按照消息的发送顺序来消费(FIFO)。RocketMQ 可以严格的保证消息有序,可以分为分区有序或者全局有序。

2、顺序消费的原理解析

在默认的情况下消息发送会采取 Round Robin 轮询方式把消息发送到不同的 queue (分区队列),而消费消息的时候从多个 queue上拉取消息,这种情况发送和消费是不能保证顺序。但是如果控制发送的顺序消息只依次发送到同一个 queue 中,消费的时候只从这个 queue 上依次拉取,则就保证了顺序。当发送和消费参与的 queue 只有一个,则是全局有序;如果多个 queue 参与,则为分区有序,即相对每个 queue,消息都是有序的。

3、下面用订单进行分区有序的示例。一个订单的顺序流程是:创建、付款、推送、完成。订单号相同的消息会被先后发送到同一个队列中,消费时,同一个 Orderld 获取到的肯定是同一个队列。

二、RocketMQ 顺序消息发送者

1、在工程 rocketmq_demo (模块)中,创建 订单构建 实体类 OrderStep.java

bash 复制代码
/**
 *  D:\java-test\idea2019\rocketmq_demo\src\main\java\djh\it\mq\rocketmq\order\OrderStep.java
 *
 *  2024-6-2 创建 订单构建 实体类 OrderStep.java
 */

package djh.it.mq.rocketmq.order;

import java.util.ArrayList;
import java.util.List;

public class OrderStep {

    private long orderId;  //订单 id
    private String desc;   //订单描述

    public long getOrderId() {
        return orderId;
    }

    public void setOrderId(long orderId) {
        this.orderId = orderId;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "OrderStep{" +
                "orderId=" + orderId +
                ", desc='" + desc + '\'' +
                '}';
    }

    public static List<OrderStep> buildOrders() {

        // 1039L    : 创建 付款 推送 完成
        // 1065L    : 创建 付款
        // 7235L    : 创建 付款

        List<OrderStep> orderList = new ArrayList<OrderStep>();

        OrderStep orderDemo = new OrderStep();
        orderDemo.setOrderId(1039L);
        orderDemo.setDesc("创建");
        orderList.add(orderDemo);

        orderDemo = new OrderStep();
        orderDemo.setOrderId(1065L);
        orderDemo.setDesc("创建");
        orderList.add(orderDemo);

        orderDemo = new OrderStep();
        orderDemo.setOrderId(1039L);
        orderDemo.setDesc("付款");
        orderList.add(orderDemo);

        orderDemo = new OrderStep();
        orderDemo.setOrderId(7235L);
        orderDemo.setDesc("创建");
        orderList.add(orderDemo);

        orderDemo = new OrderStep();
        orderDemo.setOrderId(1065L);
        orderDemo.setDesc("付款");
        orderList.add(orderDemo);

        orderDemo = new OrderStep();
        orderDemo.setOrderId(7235L);
        orderDemo.setDesc("付款");
        orderList.add(orderDemo);

        orderDemo = new OrderStep();
        orderDemo.setOrderId(1065L);
        orderDemo.setDesc("完成");
        orderList.add(orderDemo);

        orderDemo = new OrderStep();
        orderDemo.setOrderId(1039L);
        orderDemo.setDesc("推送");
        orderList.add(orderDemo);

        orderDemo = new OrderStep();
        orderDemo.setOrderId(7235L);
        orderDemo.setDesc("完成");
        orderList.add(orderDemo);

        orderDemo = new OrderStep();
        orderDemo.setOrderId(1039L);
        orderDemo.setDesc("完成");
        orderList.add(orderDemo);

        return orderList;

    }
}

2、在工程 rocketmq_demo (模块)中,创建 顺序消息发送 类 Producer.java

bash 复制代码
/**
 *   rocketmq_demo\src\main\java\djh\it\mq\rocketmq\order\Producer.java
 *
 *   2024-6-2  创建 顺序消息发送 类 Producer.java
 */
package djh.it.mq.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 java.util.List;


public class Producer {

    public static void main(String[] args) throws Exception {
       
		//1.创建消息生产者 producer,并制定生产者组名
		DefaultMQProducer producer = new DefaultMQProducer("group1");

		//2.指定 Nameserver 地址(集群配置)
		//producer.setNamesrvAddr("192.168.25.135:9876;192.168.25.138:9876");

		//2.指定 Nameserver 地址(非集群配置)
		producer.setNamesrvAddr("172.18.30.110:9876");

		//3.启动 producer
		producer.start();

		//构建消息集合
		List<OrderStep> orderSteps = OrderStep.buildOrders();

		//发送消息
		for(int i=0; i<orderSteps.size(); i++){
			String body = orderSteps.get(i)+"";
			//4.创建消息对象,指定主题 Topic、Tag 和消息体
			//参数一:消息对象, 参数二:消息队列选择器, 参数三:选择队列的业务标识(订单id)
			Message message = new Message("OrderTopic", "Order", "i"+i, body.getBytes());
			
			//5.发送 异步 消息
			SendResult sendResult = producer.send(message, new MessageQueueSelector(){

				/**
				 *
				 * @param mqs :队列集合
				 * @param msg :消息对象				 *
				 * @param arg :业务标识的参数
				 * @return
				 */
				public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
					long orderId = (Long) arg;
					long index = orderId % mqs.size();
					return mqs.get((int) index);
				}
			}, orderSteps.get(i).getOrderId());

			System.out.println("发送结果:"+sendResult);
		}
		//6.关闭生产者 producer。
	    producer.shutdown();  	   
    }
}

三、RocketMQ 顺序消息消费者

1、在工程 rocketmq_demo (模块)中,创建 顺序消息消费 类 Consumer.java

bash 复制代码
/**
 *   rocketmq_demo\src\main\java\djh\it\mq\rocketmq\order\Consumer.java
 *
 *   2024-6-2  创建 顺序消息消费 类 Consumer.java 。
 */
package djh.it.mq.rocketmq.order;

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.*;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;

import java.util.List;


public class Consumer {

	public static void main(String[] args) throws Exception {

		//1.创建消费者 Consumer,制定消费者组名
		DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");

		//2.指定 Nameserver 地址(集群配置)
		//consumer.setNamesrvAddr("192.168.25.135:9876;192.168.25.138:9876");

		//2.指定 Nameserver 地址(非集群配置)
		consumer.setNamesrvAddr("172.18.30.110:9876");

		//3.订阅主题 Topic 和 Tag
		consumer.subscribe("OrderTopic", "*");  //接收所有消息。

		//4.注册消息监听器
		consumer.registerMessageListener(new MessageListenerOrderly() {
			public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
				for(MessageExt msg : msgs){
					System.out.println("线程名称:【"+Thread.currentThread().getName() + "】 消费消息:" + new String(msg.getBody()));  //转换为字符串消息
				}

				return ConsumeOrderlyStatus.SUCCESS;
			}
		});

		//5.启动消费者 consumer。
		consumer.start();
		System.out.println("消费消息启动了");
	}
}

2、先启动 顺序消息发送 类 Producer.java,再启动 顺序消息消费 类 Consumer.java 进行测试。

四、RocketMQ 延迟消息

1、RocketMQ 延迟消息

比如电商里,提交了一个订单就可以发送一个延时消息,1h 后去检查这个订单的状态,

如果还是未付款就取消订单释放库存。

2、RocketMQ 延迟消息 使用限制

//org/apache/rocketmg/store/config/Messagestoreconfig.java

private string messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h".

现在 RocketMg 并不支持任意时间的延时,需要设置几个固定的延时等级,从 1s 到 2h 分别对应着等级 1 到 18。

3、在工程 rocketmq_demo (模块)中,创建 延迟消息 发送 类 Producer.java

bash 复制代码
/**
 *   rocketmq_demo\src\main\java\djh\it\mq\rocketmq\delay\Producer.java
 *
 *   2024-6-2  创建 延迟消息 发送 类 Producer.java
 */
package djh.it.mq.rocketmq.delay;

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import java.util.concurrent.TimeUnit;

public class Producer {

    public static void main(String[] args) throws Exception {
       
		//1.创建消息生产者 producer,并制定生产者组名
		DefaultMQProducer producer = new DefaultMQProducer("group1");

		//2.指定 Nameserver 地址(集群配置)
		//producer.setNamesrvAddr("192.168.25.135:9876;192.168.25.138:9876");

		//2.指定 Nameserver 地址(非集群配置)
		producer.setNamesrvAddr("172.18.30.110:9876");

		//3.启动 producer
		producer.start();

		//发送消息
		for(int i=0; i<10; i++){
			//4.创建消息对象,指定主题 Topic、Tag 和消息体
			//参数一:消息主题 Topic, 参数二:消息 Tag, 参数三:消息内容
			Message msg = new Message("DelayTopic", "Tag1", ("Hello World"+i).getBytes());

			//设定延迟发送 时间为 5 秒(目前 rocketmq 支持的延迟等级:"1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h".)
			msg.setDelayTimeLevel(2);

			//5.发送消息
			SendResult result = producer.send(msg);

			System.out.println("发送结果:"+result);
			TimeUnit.SECONDS.sleep(1);  //线程睡1秒
		}
		//6.关闭生产者 producer。
		producer.shutdown();
    }
}

4、在工程 rocketmq_demo (模块)中,创建 延迟消息 消费 类 Consumer.java 。

bash 复制代码
/**
 *   rocketmq_demo\src\main\java\djh\it\mq\rocketmq\delay\Consumer.java
 *
 *   2024-6-2  创建 延迟消息 消费 类 Consumer.java 。
 */
package djh.it.mq.rocketmq.delay;

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.*;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;

import java.util.List;

public class Consumer {

	public static void main(String[] args) throws Exception {

		//1.创建消费者 Consumer,制定消费者组名
		DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");

		//2.指定 Nameserver 地址(集群配置)
		//consumer.setNamesrvAddr("192.168.25.135:9876;192.168.25.138:9876");

		//2.指定 Nameserver 地址(非集群配置)
		consumer.setNamesrvAddr("172.18.30.110:9876");

		//3.订阅主题 Topic 和 Tag
		//consumer.subscribe("base", "Tag1");  //接收同步消息
		//consumer.subscribe("base", "Tag2");  //接收异步消息前,可以让先发送异步消息。
		//consumer.subscribe("base", "Tag1 | Tag2");  //接收同步消息 和 异步消息
		consumer.subscribe("DelayTopic", "*");  //接收所有消息。

		//添加消费模式
		//consumer.setMessageModel(MessageModel.CLUSTERING);  //默认是负载均衡模式消费
		consumer.setMessageModel(MessageModel.BROADCASTING);  //广播模式消费

		//4.设置回调函数,处理消息
		consumer.registerMessageListener(new MessageListenerConcurrently(){
			//接受消息内容
			public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context){
				//System.out.println(msgs); //接收到的消息是未转换的字节码

				for(MessageExt msg : msgs){
					System.out.println("消息ID:【" + msg.getMsgId()+"】,延迟时间:"+(System.currentTimeMillis()-msg.getStoreTimestamp()));  //转换为字符串消息
				}

				return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
			}
		});

		//5.启动消费者 consumer。
		consumer.start();

		System.out.println("消费者启动");
	}
}

5、先启动 延迟消息 消费 类 Consumer.java 再启动 延迟消息 发送 类 Producer.java 进行测试。

五、RocketMQ 批量消息发送

1、批量发送消息能显著提高传递小消息的性能。限制是这些批量消息应该有相同的 topic,相同的 waitstoreMsgOK,而且不能是延时消息。此外,这一批消息的总大小不应超过無 4MB。如果消息的总长度可能大于4MB时,这时候最好把消息进行分割。

2、在工程 rocketmq_demo (模块)中,创建 批量消息发送 发送 类 Producer.java

bash 复制代码
/**
 *   rocketmq_demo\src\main\java\djh\it\mq\rocketmq\batch\Producer.java
 *
 *   2024-6-2  创建 批量消息发送的 发送 类 Producer.java
 */
package djh.it.mq.rocketmq.batch;

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class Producer {

    public static void main(String[] args) throws Exception {
       
		//1.创建消息生产者 producer,并制定生产者组名
		DefaultMQProducer producer = new DefaultMQProducer("group1");

		//2.指定 Nameserver 地址(集群配置)
		//producer.setNamesrvAddr("192.168.25.135:9876;192.168.25.138:9876");

		//2.指定 Nameserver 地址(非集群配置)
		producer.setNamesrvAddr("172.18.30.110:9876");

		//3.启动 producer
		producer.start();

		//创建一个集合
		List<Message> msgs = new ArrayList<Message>();

		//发送消息

		//4.创建消息对象,指定主题 Topic、Tag 和消息体
		//参数一:消息主题 Topic, 参数二:消息 Tag, 参数三:消息内容
		Message msg1 = new Message("BatchTopic", "Tag1", ("Hello World"+1).getBytes());
		Message msg2 = new Message("BatchTopic", "Tag1", ("Hello World"+2).getBytes());
		Message msg3 = new Message("BatchTopic", "Tag1", ("Hello World"+3).getBytes());

		msgs.add(msg1);
		msgs.add(msg2);
		msgs.add(msg3);

		//5.发送消息
		SendResult result = producer.send(msgs);

		System.out.println("发送结果:"+result);
		TimeUnit.SECONDS.sleep(1);  //线程睡1秒

		//6.关闭生产者 producer。
		producer.shutdown();
    }
}

3、在工程 rocketmq_demo (模块)中,创建 批量消息发送 消费 类 Consumer.java 。

bash 复制代码
/**
 *   rocketmq_demo\src\main\java\djh\it\mq\rocketmq\batch\Consumer.java
 *
 *   2024-6-2  创建 批量消息发送 消费 类 Consumer.java 。
 */
package djh.it.mq.rocketmq.batch;

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;

import java.util.List;

public class Consumer {

	public static void main(String[] args) throws Exception {

		//1.创建消费者 Consumer,制定消费者组名
		DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");

		//2.指定 Nameserver 地址(集群配置)
		//consumer.setNamesrvAddr("192.168.25.135:9876;192.168.25.138:9876");

		//2.指定 Nameserver 地址(非集群配置)
		consumer.setNamesrvAddr("172.18.30.110:9876");

		//3.订阅主题 Topic 和 Tag
		consumer.subscribe("BatchTopic", "*");  //接收所有消息。

		//4.设置回调函数,处理消息
		consumer.registerMessageListener(new MessageListenerConcurrently(){
			//接受消息内容
			public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context){
			//System.out.println(msgs); //接收到的消息是未转换的字节码

			for(MessageExt msg : msgs){
				System.out.println("consumeThread=" + Thread.currentThread().getName()+", "+new String(msg.getBody()));  //转换为字符串消息
			}

			return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
			}
		});

		//5.启动消费者 consumer。
		consumer.start();

		System.out.println("消费者启动");
	}
}

4、先启动 批量消息发送 消费 类 Consumer.java 再启动 批量消息发送 发送 类 Producer.java 进行测试。

上一节关联链接请点击:
# 全面解剖 消息中间件 RocketMQ-(3)

相关推荐
yylの博客1 小时前
Windows通过git-bash安装zsh
windows·git·bash·zsh
进击的code2 小时前
windows 下使用WLS2 编译aosp Android14并刷机到pixle 5a
windows
染指11105 小时前
50.第二阶段x86游戏实战2-lua获取本地寻路,跨地图寻路和获取当前地图id
c++·windows·lua·游戏安全·反游戏外挂·游戏逆向·luastudio
dntktop6 小时前
Converseen:全能免费批量图像处理专家
windows
一个懒鬼8 小时前
Windows脚本清理C盘缓存
windows·缓存
蚁景网络安全9 小时前
Cobalt Strike 4.8 用户指南-第十四节 Aggressor 脚本
windows·microsoft
Camllia379 小时前
电脑运行库DirectX出问题怎么办?
windows·经验分享·电脑
总是学不会.10 小时前
【集合】Java 8 - Stream API 17种常用操作与案例详解
java·windows·spring boot·mysql·intellij-idea·java集合
CW_ZB66611 小时前
【无标题】
windows
0xdadream11 小时前
powershell美化
windows