RocketMQ-顺序消费
看了https://www.jianshu.com/p/453c6e7ff81c这篇博客,得出顺序消费的结论。“要实现严格的顺序消息,简单且可行的办法就是:保证生产者 - MQServer - 消费者是一对一的关系”。
我们下面通过几个实例来学习RocketMQ的顺序消费。
一、单节点,也就是一个Producer一个Consumer。
操作步骤:
1、先启动ConsumerQueue1
2、再启动ProducerQueue
Producer端:
package org.hope.lee.consumer.queue; import com.alibaba.rocketmq.client.exception.MQBrokerException;
import com.alibaba.rocketmq.client.exception.MQClientException;
import com.alibaba.rocketmq.client.producer.DefaultMQProducer;
import com.alibaba.rocketmq.client.producer.MessageQueueSelector;
import com.alibaba.rocketmq.client.producer.SendResult;
import com.alibaba.rocketmq.common.message.Message;
import com.alibaba.rocketmq.common.message.MessageQueue;
import com.alibaba.rocketmq.remoting.exception.RemotingException; import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List; public class ProducerQueue {
public static void main(String[] args) {
String group_name = "order_producer";
DefaultMQProducer producer = new DefaultMQProducer(group_name);
producer.setNamesrvAddr("192.168.31.176:9876;192.168.31.165:9876");
try {
producer.start();
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = sdf.format(date);
/**
* 第一个队列
* 通过 public SendResult send(Message msg, MessageQueueSelector selector, Object arg)来指定发送消息到哪个队列
*/
for(int i = 1; i <= 5; i++) {
String body = dateStr + "body_1_" + i;
Message message = new Message("TopicTest", "order1", "KEY" + i, body.getBytes());
SendResult sendResult = producer.send(message, new MessageQueueSelector() {
public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
Integer id = (Integer) o;
return list.get(id);
}
}, 0); //0是队列的下标
System.out.println(sendResult + ", body:" + body);
} producer.shutdown(); } catch (MQClientException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (RemotingException e) {
e.printStackTrace();
} catch (MQBrokerException e) {
e.printStackTrace();
}
}
}
Consumer端:
package org.hope.lee.producer.queue; import com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer;
import com.alibaba.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import com.alibaba.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import com.alibaba.rocketmq.client.consumer.listener.MessageListenerOrderly;
import com.alibaba.rocketmq.common.consumer.ConsumeFromWhere;
import com.alibaba.rocketmq.common.message.MessageExt; import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit; public class ConsumerQueue1 { public ConsumerQueue1() throws Exception {
String group_name = "order_consumer";
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(group_name);
consumer.setNamesrvAddr("192.168.31.176:9876;192.168.31.165:9876");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//订阅的主题, 以及过滤的标签内容
consumer.subscribe("TopicTest", "*");
//注册监听
consumer.registerMessageListener(new Listener());
consumer.start();
System.out.println("Consumer Started.....");
} /**
* 这里实现MessageListenerOrderLy接口就是为了达到顺序消费的目的,
* 如果是使用MessageListenerConcurrently,则需要把线程池改为单线程模式。
* 但是也不能保证说一定会顺序消费,因为如果master宕机了,导致写入队列的数量上
* 出现变化。
*
* 从消费端,如果想保证这批消息是M1消费完成再消费M2的话,可以使用MessageListenerOrderly接口,但是这样的话会有以下问题:
* 1. 遇到消息失败的消息,无法跳过,当前队列消费暂停
* 2. 目前版本的RocketMQ的MessageListenerOrderly是不能从slave消费消息的。
*/
class Listener implements MessageListenerOrderly {
private Random random = new Random(); public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext context) {
context.setAutoCommit(true);
for(MessageExt msg : list) {
System.out.println(msg + ", content:" + new String(msg.getBody()));
}
try {
TimeUnit.SECONDS.sleep(random.nextInt(5)); //随机休眠时间,模拟业务处理时间 } catch (InterruptedException e) {
e.printStackTrace();
}
return ConsumeOrderlyStatus.SUCCESS;
}
}
public static void main(String[] args) throws Exception {
ConsumerQueue1 c = new ConsumerQueue1(); }
}
Consuerm端输出结果:(横向拖到最后看蓝色字体)
Consumer Started.....
MessageExt [queueId=0, storeSize=159, queueOffset=0, sysFlag=0, bornTimestamp=1515420522468, bornHost=/192.168.31.38:9357, storeTimestamp=1515420551480, storeHost=/192.168.31.165:10911, msgId=C0A81FA500002A9F00000000000001EF, commitLogOffset=495, bodyCRC=829956747, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message [topic=TopicTest, flag=0, properties={TAGS=order1, KEYS=KEY1, WAIT=true, MAX_OFFSET=5, MIN_OFFSET=0}, body=27]], content:2018-01-08 22:08:42body_1_1
MessageExt [queueId=0, storeSize=159, queueOffset=1, sysFlag=0, bornTimestamp=1515420522668, bornHost=/192.168.31.38:9357, storeTimestamp=1515420551683, storeHost=/192.168.31.165:10911, msgId=C0A81FA500002A9F000000000000028E, commitLogOffset=654, bodyCRC=678523697, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message [topic=TopicTest, flag=0, properties={TAGS=order1, KEYS=KEY2, WAIT=true, MAX_OFFSET=5, MIN_OFFSET=0}, body=27]], content:2018-01-08 22:08:42body_1_2
MessageExt [queueId=0, storeSize=159, queueOffset=2, sysFlag=0, bornTimestamp=1515420522781, bornHost=/192.168.31.38:9357, storeTimestamp=1515420551716, storeHost=/192.168.31.165:10911, msgId=C0A81FA500002A9F000000000000032D, commitLogOffset=813, bodyCRC=1601586087, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message [topic=TopicTest, flag=0, properties={TAGS=order1, KEYS=KEY3, WAIT=true, MAX_OFFSET=5, MIN_OFFSET=0}, body=27]], content:2018-01-08 22:08:42body_1_3
MessageExt [queueId=0, storeSize=159, queueOffset=3, sysFlag=0, bornTimestamp=1515420522792, bornHost=/192.168.31.38:9357, storeTimestamp=1515420551753, storeHost=/192.168.31.165:10911, msgId=C0A81FA500002A9F00000000000003CC, commitLogOffset=972, bodyCRC=1091753476, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message [topic=TopicTest, flag=0, properties={TAGS=order1, KEYS=KEY4, WAIT=true, MAX_OFFSET=5, MIN_OFFSET=0}, body=27]], content:2018-01-08 22:08:42body_1_4
MessageExt [queueId=0, storeSize=159, queueOffset=4, sysFlag=0, bornTimestamp=1515420522833, bornHost=/192.168.31.38:9357, storeTimestamp=1515420551768, storeHost=/192.168.31.165:10911, msgId=C0A81FA500002A9F000000000000046B, commitLogOffset=1131, bodyCRC=907404946, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message [topic=TopicTest, flag=0, properties={TAGS=order1, KEYS=KEY5, WAIT=true, MAX_OFFSET=5, MIN_OFFSET=0}, body=27]], content:2018-01-08 22:08:42body_1_5
一、Consumer端集群消费的顺序消费,也就是一个Producer多个Consumer。
步骤一、我们先改造一下ProducerQueue,在第一个队列下面再加两个队列
/**
* 第二个队列
*/
for(int i = 1; i <= 5; i++) {
//时间戳
String body = dateStr + "order_2" + i;
Message message = new Message("TopicTest", "body_2_", "KEY" + i, body.getBytes());
SendResult sendResult = producer.send(message, new MessageQueueSelector() {
public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
Integer id = (Integer) o;
return list.get(id);
}
}, 1); //1是队列的下标
System.out.println(sendResult + ", body:" + body);
}
/**
* 第三个队列
*/
for(int i = 1; i <= 5; i++) {
//时间戳
String body = dateStr + "order_3" + i;
Message message = new Message("TopicTest", "body_3_", "KEY" + i, body.getBytes());
SendResult sendResult = producer.send(message, new MessageQueueSelector() {
public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
Integer id = (Integer) o;
return list.get(id);
}
}, 2); //2是队列的下标
System.out.println(sendResult + ", body:" + body);
}
步骤二、再创建一个消费端ConsumerQueue2,代码跟ConsumerQueue1一样,这里就不重复了。
步骤三、启动ConsumerQueue1和ConsumerQueue3
步骤四、启动ProducerQueue
结果:




从结果中看到,两个消费端都是按照队列顺序消费的,并且负载均衡,ConsumerQueue1消费了第三个队列,ConsumerQueue2消费了第一个队列和第二个队列。
需要注意的一点是,对于顺序消费,我们是不能再Consumer端再使用多线程去消费的。这样就破坏了顺序消费的生态环境。
RocketMQ-顺序消费的更多相关文章
- RocketMQ(7)---RocketMQ顺序消费
RocketMQ顺序消费 如果要保证顺序消费,那么他的核心点就是:生产者有序存储.消费者有序消费. 一.概念 1.什么是无序消息 无序消息 无序消息也指普通的消息,Producer 只管发送消息,Co ...
- 一次 RocketMQ 顺序消费延迟的问题定位
一次 RocketMQ 顺序消费延迟的问题定位 问题背景与现象 昨晚收到了应用报警,发现线上某个业务消费消息延迟了 54s 多(从消息发送到MQ 到被消费的间隔): 2021-06-30T23:12: ...
- RocketMQ 顺序消费只消费一次 坑
rocketMq实现顺序消费的原理 produce在发送消息的时候,把消息发到同一个队列(queue)中,消费者注册消息监听器为MessageListenerOrderly,这样就可以保证消费端只有一 ...
- RocketMq顺序消费
部分内容出处 https://www.jianshu.com/p/453c6e7ff81c rocketmq内部有4个默认的队里,在发送消息时,同一组的消息需要按照顺序,发送到相应的mq中,同一组 ...
- 51.RocketMQ 顺序消费
大部分的员工早上的心情可能不会很好,因为这时想到还有很多事情要做,压力会大点,一般到下午4点左右,状态会是一天中最好的,因为这时大部分的工作做得差不多了,又快要下班了,当然也不是绝对.要注意记录各下属 ...
- RocketMQ专题2:三种常用生产消费方式(顺序、广播、定时)以及顺序消费源码探究
顺序.广播.定时任务 前插 在进行常用的三种消息类型例子展示的时候,我们先来说一说RocketMQ的几个重要概念: PullConsumer与PushConsumer:主要区别在于Pull与Pus ...
- RocketMQ事务消费和顺序消费详解
一.RocketMq有3中消息类型 1.普通消费 2. 顺序消费 3.事务消费 顺序消费场景 在网购的时候,我们需要下单,那么下单需要假如有三个顺序,第一.创建订单 ,第二:订单付款,第三:订单完成. ...
- 【转】RocketMQ事务消费和顺序消费详解
RocketMQ事务消费和顺序消费详解 转载说明:该文章纯转载,若有侵权或给原作者造成不便望告知,仅供学习参考. 一.RocketMq有3中消息类型 1.普通消费 2. 顺序消费 3.事务消费 顺序消 ...
- 分布式消息队列RocketMQ&Kafka -- 消息的“顺序消费”
在说到消息中间件的时候,我们通常都会谈到一个特性:消息的顺序消费问题.这个问题看起来很简单:Producer发送消息1, 2, 3... Consumer按1, 2, 3...顺序消费. 但实际情况却 ...
- RocketMQ的顺序消费和事务消费
一.三种消费 :1.普通消费 2. 顺序消费 3.事务消费 1.1 顺序消费:在网购的时候,我们需要下单,那么下单需要假如有三个顺序,第一.创建订单 ,第二:订单付款,第三:订单完成.也就是这个三个 ...
随机推荐
- Sublime Text3注册码,亲测可用
将以下复制在输入框即可 ,亲测可用 . 不过还是希望大家多多支持正版 . -– BEGIN LICENSE -– TwitterInc 200 User License EA7E-890007 1D7 ...
- AspNet Core Web 应用程序的启动 当项目中 没有Startup.cs 类如何设置启动 配置等等
感叹: Core 16年6月1号 在中国宣布上线 到现在已经快经历两年时间了,目前版本已经到了2.0 就目前的前景来看,个人感觉 到2020年才可能有所起色,等到Core更成熟 个人看法:在.net这 ...
- 浅谈 URI 及其转义
URI URI,全称是 Uniform Resource Identifiers,即统一资源标识符,用于在互联网上标识一个资源,比如 https://www.upyun.com/products/cd ...
- MyEclipse2015上传项目到GitHub(很详细)
MyEclipse 2015 默认已经安装了git插件,在MyEclipse中上传项目到github的步骤如下: 1.github官网(https://github.com)申请开通账号(略) 1.1 ...
- org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression
前言 本文中提到的解决方案,源码地址在:springboot-thymeleaf,希望可以帮你解决问题. 本文中涉及的两个异常为我开发时遇到的,可能和你目前所要处理的bug不同,如果不是同一个问题,希 ...
- Hyperledger Fabric Model——超级账本组成模型
超级账本组成模型 本文主要讲述Hyperledger Fabric的关键设计特性,并细述如何实现了一个全面的.可定制的企业级区块链解决方案: 资产定义--资产这里理解为任何具有货币价值的东西,它们都可 ...
- HTML元素分类【三种类型】
在CSS中,html中的标签元素大体被分为三种不同的类型: 块状元素.内联元素(又叫行内元素)和内联块状元素. 块状元素:display:block内联元素:display:inline 内联块状 ...
- 解析库-beautifulsoup模块
# -*- coding: utf-8 -*- from bs4 import BeautifulSoup # 安装:pip install beautifulsoup4 # Beautiful So ...
- 渗透测试入门DVWA 教程1:环境搭建
首先欢迎新萌入坑.哈哈.你可能抱着好奇心或者疑问.DVWA 是个啥? DVWA是一款渗透测试的演练系统,在圈子里是很出名的.如果你需要入门,并且找不到合适的靶机,那我就推荐你用DVWA. 我们通常将演 ...
- [bzoj3702] 二叉树
一个节点的儿子是否交换,不会影响到它和兄弟节点间的逆序对数. 所以每次合并线段树的时候算一下交换与不交换的逆序对数,然后选个较小值就行了. #include<cstdio> #includ ...