1、定义
顺序消息(FIFO 消息):是 MQ 提供的一种严格按照顺序进行发布和消费的消息类型。
顺序消息由两个部分组成:顺序发布和顺序消费。
顺序消息包含两种类型:
- 分区顺序:一个Partition内所有的消息按照先进先出的顺序进行发布和消费
- 全局顺序:一个Topic内所有的消息按照先进先出的顺序进行发布和消费
2、代码示例
2.1、消息实体
package com.wzl.rocketmq.order;
import java.util.ArrayList;
import java.util.List;
/**
* 消息体
*/
public class OrderStep {
private long orderId;
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;
}
public static List<OrderStep> buildOrders() {
List<OrderStep> list = new ArrayList<>();
OrderStep orderStep = new OrderStep();
orderStep.setOrderId(101L);
orderStep.setDesc("创建");
list.add(orderStep);
orderStep = new OrderStep();
orderStep.setOrderId(101L);
orderStep.setDesc("付款");
list.add(orderStep);
orderStep = new OrderStep();
orderStep.setOrderId(102L);
orderStep.setDesc("创建");
list.add(orderStep);
orderStep = new OrderStep();
orderStep.setOrderId(101L);
orderStep.setDesc("发货");
list.add(orderStep);
orderStep = new OrderStep();
orderStep.setOrderId(102L);
orderStep.setDesc("发货");
list.add(orderStep);
orderStep = new OrderStep();
orderStep.setOrderId(101L);
orderStep.setDesc("结束");
list.add(orderStep);
orderStep = new OrderStep();
orderStep.setOrderId(102L);
orderStep.setDesc("结束");
list.add(orderStep);
return list;
}
@Override
public String toString() {
return "OrderStep{" +
"orderId=" + orderId +
", desc='" + desc + '\'' +
'}';
}
}
2.2、生产者
package com.wzl.rocketmq.order;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
/**
* 顺序消息--生产者
*/
public class OrderProducer {
public static void sendTagAMessage() throws MQClientException {
//定义producer
DefaultMQProducer producer = new DefaultMQProducer("order_group101");
//要设置NameServer地址,多个;分割
producer.setNamesrvAddr("192.168.12.121:9876");
//启动
producer.start();
//发送消息
for (int i = 0; i < OrderStep.buildOrders().size(); i++) {
try {
OrderStep orderStep = OrderStep.buildOrders().get(i);
String body = orderStep + "";
Message msg = new Message("OrderTopic", "TagOrder", "i" + i, body.getBytes());
SendResult sendResult = producer.send(msg, (mqs, message, args) -> {
long orderId = (long) args;
long index = orderId % mqs.size();//订单号和queue数量进行取模
return mqs.get((int) index);
}, orderStep.getOrderId());
System.out.println("OrderProducer发送消息Order:" + sendResult);
} catch (Exception e) {
e.printStackTrace();
}
}
//关闭生产者
producer.shutdown();
}
public static void main(String[] args) throws MQClientException {
//发送tagA消息
sendTagAMessage();
}
}
2.3、消费者
package com.wzl.rocketmq.order;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
/**
* 顺序消息--消费者
*/
public class OrderConsumer {
public static void main(String[] args) throws MQClientException {
//声明并初始化一个consumer
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("order_consumer101");
//要设置NameServer地址,多个;分割
consumer.setNamesrvAddr("192.168.12.121:9876");
//设置consumer所订阅的Topic和Tag,*代表全部的Tag
consumer.subscribe("OrderTopic", "TagOrder");
//集群订阅
consumer.setMessageModel(MessageModel.CLUSTERING);
//这里设置的是一个consumer的消费策略
//CONSUME_FROM_LAST_OFFSET 默认策略,从该队列最尾开始消费,即跳过历史消息
//CONSUME_FROM_FIRST_OFFSET 从队列最开始开始消费,即历史消息(还储存在broker的)全部消费一遍
//CONSUME_FROM_TIMESTAMP 从某个时间点开始消费,和setConsumeTimestamp()配合使用,默认是半个小时以前
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
//设置一个Listener,主要进行消息的逻辑处理
consumer.registerMessageListener((MessageListenerOrderly) (list, context) -> {
for (MessageExt messageExt : list) {
String messageBody = new String(messageExt.getBody());
System.out.println("线程名称: " + Thread.currentThread().getName() + " 消费消息: " + messageBody);//输出消息内容
}
//返回消费状态
return ConsumeOrderlyStatus.SUCCESS;
});
//调用start()方法启动consumer
consumer.start();
System.out.println("OrderConsumer Started.");
}
}
2.3、测试结果
生产者发送的消息都是刻意被打乱的
为了方便观察,我们启动2个消费者
结果发现:由于具有相同的OrderId取模在同一个queue中,消费也是不同订单号被不同的消费者有序消费的。