消息中间件RocketMq之顺序消费
当前情况说明
-
同一个topic 有4个队列
-
队列先天支持FIFO模型,单一生产和消费者下只要保证使用
MessageListenerOrderly
监听器即可
要求
- 保证有序参与因素
- FIFO
- 队列内保证有序
- 一个线程发送消息
- 一个消费线程
顺序消费表示消息消费的顺序同生产者为每个消息队列发送的顺序一致,所以如果正在处理全局顺序是强制性的场景,需要确保使用的主题只有一个消息队列。
并行消费不再保证消息顺序,消费的最大并行数量受每个消费者客户端指定的线程池限制。
那么只要顺序的发送,再保证一个线程只去消费一个队列上的消息,那么他就是有序的。
使用
- 生产者
- MessageQueueSelector接口
跟普通消息相比,顺序消息的使用需要在producer的send()方法中添加MessageQueueSelector接口的实现类,并重写select选择使用的队列,因为顺序消息局部顺序,需要将所有消息指定发送到同一队列中。
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("myProducer01");
// 设置nameserver 地址
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
// topic 消息发送的地址
// body 消息具体内容
new Thread(() -> {
for (int i = 0; i < 1000000; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 实现接口
MessageQueueSelector messageQueueSelector = new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> list, Message message, Object arg) {
// 手动选择Queue,向topic中哪个queue写消息
return list.get(0);
}
};
Message msg0 = new Message("mysortTopic001", ("我是消息" + i + "=>" + LocalDateTime.now()).getBytes());
// 单个发送
msg0.putUserProperty("age",String.valueOf(i));
SendResult sendResult1 = null;
try {
// 传递参数
sendResult1 = producer.send(msg0,messageQueueSelector,i);
} catch (MQClientException e) {
e.printStackTrace();
} catch (RemotingException e) {
e.printStackTrace();
} catch (MQBrokerException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("单个发送" + sendResult1);
}
producer.shutdown();
System.out.println("关闭");
}).start();
}
- 消费
- 注册监听器时 使用MessageListenerOrderly
- 使用一个线程进行消费
public static void main(String[] args)throws Exception {
// 消费组
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("myConsumer01");
consumer.setNamesrvAddr("127.0.0.1:9876");
// 每个consumer只能关注一个topic
// topic 关注的消息主题
// 过滤器 * 表示不过滤
//consumer.subscribe("myTopic001", *);
//tag过滤
// 同一消费组,Tag 过滤得一致
//consumer.subscribe("TopicTest", "TagA");
// MessageSelector messageSelector = MessageSelector.bySql("age >10 and age < 20");
consumer.subscribe("mysortTopic001", "*");
// 最大消费线程树
consumer.setConsumeThreadMax(1);
// 最小消费线程树
consumer.setConsumeThreadMin(1);
// 注册消息监听
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
for (MessageExt msg : list) {
System.out.println(new String(msg.getBody())+ "=》消费时间:" + LocalDateTime.now());;
}
// 默认情况下这条消息只会被一个consumer消费 点对点的
// message 状态修改
// ack 确认消费
return ConsumeOrderlyStatus.SUCCESS;
}
});
// 设置消费模式,
// CLUSTERING 集群模式,只消费一次
// BROADCASTING 广播模式 可多次消费
consumer.setMessageModel(MessageModel.CLUSTERING);
consumer.start();
System.out.println("Consumer 02 start...");
}