# 全面解剖 消息中间件 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)

相关推荐
cpsvps_net14 小时前
美国服务器环境下Windows容器工作负载智能弹性伸缩
windows
甄超锋14 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
cpsvps17 小时前
美国服务器环境下Windows容器工作负载基于指标的自动扩缩
windows
网硕互联的小客服20 小时前
Apache 如何支持SHTML(SSI)的配置方法
运维·服务器·网络·windows·php
etcix20 小时前
implement copy file content to clipboard on Windows
windows·stm32·单片机
许泽宇的技术分享20 小时前
Windows MCP.Net:基于.NET的Windows桌面自动化MCP服务器深度解析
windows·自动化·.net
非凡ghost21 小时前
AMS PhotoMaster:全方位提升你的照片编辑体验
windows·学习·信息可视化·软件需求
mortimer1 天前
一次与“顽固”外部程序的艰难交锋:subprocess 调用exe踩坑实录
windows·python·ai编程
gameatp1 天前
从 Windows 到 Linux 服务器的全自动部署教程(免密登录 + 压缩 + 上传 + 启动)
linux·服务器·windows
穷人小水滴1 天前
在 windows 运行 flatpak 应用 (WSL)
linux·windows·ubuntu