rocketMq实现顺序消费的原理

produce在发送消息的时候,把消息发到同一个队列(queue)中,消费者注册消息监听器为MessageListenerOrderly,这样就可以保证消费端只有一个线程去消费消息

注意:是把把消息发到同一个队列(queue),不是同一个topic,默认情况下一个topic包括4个queue

单个节点(Producer端1个、Consumer端1个)

1、Producer.java 

package order;  

import java.util.List;  

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; /**
* Producer,发送顺序消息
*/
public class Producer {
public static void main(String[] args) {
try {
DefaultMQProducer producer = new DefaultMQProducer("order_Producer");
producer.setNamesrvAddr("192.168.100.145:9876;192.168.100.146:9876;192.168.100.149:9876;192.168.100.239:9876"); producer.start(); // String[] tags = new String[] { "TagA", "TagB", "TagC", "TagD",
// "TagE" }; for (int i = 1; i <= 5; i++) { Message msg = new Message("TopicOrderTest", "order_1", "KEY" + i, ("order_1 " + i).getBytes()); SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
Integer id = (Integer) arg;
int index = id % mqs.size();
return mqs.get(index);
}
}, 0); System.out.println(sendResult);
} producer.shutdown();
} catch (MQClientException e) {
e.printStackTrace();
} catch (RemotingException e) {
e.printStackTrace();
} catch (MQBrokerException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

2、Consumer.java   (有问题)

package order;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
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.client.exception.MQClientException;
import com.alibaba.rocketmq.common.consumer.ConsumeFromWhere;
import com.alibaba.rocketmq.common.message.MessageExt; /**
* 顺序消息消费,带事务方式(应用可控制Offset什么时候提交)
*/
public class Consumer1 { public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("order_Consumer");
consumer.setNamesrvAddr("192.168.100.145:9876;192.168.100.146:9876;192.168.100.149:9876;192.168.100.239:9876"); /**
* 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br>
* 如果非第一次启动,那么按照上次消费的位置继续消费
*/
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); consumer.subscribe("TopicOrderTest", "*"); consumer.registerMessageListener(new MessageListenerOrderly() {
AtomicLong consumeTimes = new AtomicLong(0); public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
// 设置自动提交
context.setAutoCommit(true);
for (MessageExt msg : msgs) {
System.out.println(msg + ",内容:" + new String(msg.getBody()));
} try {
TimeUnit.SECONDS.sleep(5L);
} catch (InterruptedException e) { e.printStackTrace();
}
; return ConsumeOrderlyStatus.SUCCESS;
}
}); consumer.start(); System.out.println("Consumer1 Started.");
} }

这个地方有一个大坑,注册监听类的时候,不能使用匿名内部类。不然的话,只会消费一次,然后消费者就 挂了……挂了……挂了…… 

监听类要单独写!!!

正确消费者写法:

自定义监听类:

MyMessageListener

public class MyMessageListener implements  MessageListenerOrderly {

    @Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
// 设置自动提交
context.setAutoCommit(true);
for (MessageExt msg : msgs) {
System.out.println(msg + ",内容:" + new String(msg.getBody()));
} try {
TimeUnit.SECONDS.sleep(5L);
} catch (InterruptedException e) { e.printStackTrace();
} return ConsumeOrderlyStatus.SUCCESS;
}
}

Consumer.java

public class Consumer {

    public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("order_Consumer");
consumer.setNamesrvAddr("101.200.33.225:9876"); /**
* 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br>
* 如果非第一次启动,那么按照上次消费的位置继续消费
*/
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); consumer.subscribe("TopicOrderTest", "*"); MyMessageListener myMessageListener = new MyMessageListener();
consumer.registerMessageListener(myMessageListener);
consumer.start(); System.out.println("Consumer1 Started.");
}
}

参考:https://www.cnblogs.com/antain/p/rocketmq.html

http://www.cnblogs.com/520playboy/p/6750023.html

http://dbaplus.cn/news-21-1123-1.html

RocketMQ 顺序消费只消费一次 坑的更多相关文章

  1. RocketMQ(7)---RocketMQ顺序消费

    RocketMQ顺序消费 如果要保证顺序消费,那么他的核心点就是:生产者有序存储.消费者有序消费. 一.概念 1.什么是无序消息 无序消息 无序消息也指普通的消息,Producer 只管发送消息,Co ...

  2. RocketMq顺序消费

    部分内容出处   https://www.jianshu.com/p/453c6e7ff81c rocketmq内部有4个默认的队里,在发送消息时,同一组的消息需要按照顺序,发送到相应的mq中,同一组 ...

  3. 一次 RocketMQ 顺序消费延迟的问题定位

    一次 RocketMQ 顺序消费延迟的问题定位 问题背景与现象 昨晚收到了应用报警,发现线上某个业务消费消息延迟了 54s 多(从消息发送到MQ 到被消费的间隔): 2021-06-30T23:12: ...

  4. RocketMQ一个新的消费组初次启动时从何处开始消费呢?

    目录 1.抛出问题 1.1 环境准备 1.2 消息发送者代码 1.3 消费端验证代码 2.探究CONSUME_FROM_MAX_OFFSET实现原理 2.1 CONSUME_FROM_LAST_OFF ...

  5. kafka 保证消息被消费和消息只消费一次

    1. 保证消息被消费 即使消息发送到了消息队列,消息也不会万无一失,还是会面临丢失的风险. 我们以 Kafka 为例,消息在Kafka 中是存储在本地磁盘上的, 为了减少消息存储对磁盘的随机 I/O, ...

  6. 聊一聊顺序消息(RocketMQ顺序消息的实现机制)

    当我们说顺序时,我们在说什么? 日常思维中,顺序大部分情况会和时间关联起来,即时间的先后表示事件的顺序关系. 比如事件A发生在下午3点一刻,而事件B发生在下午4点,那么我们认为事件A发生在事件B之前, ...

  7. RocketMQ源码 — 十、 RocketMQ顺序消息

    RocketMQ本身支持顺序消息,在使用上发送顺序消息和非顺序消息有所区别 发送顺序消息 SendResult sendResult = producer.send(msg, new MessageQ ...

  8. 51.RocketMQ 顺序消费

    大部分的员工早上的心情可能不会很好,因为这时想到还有很多事情要做,压力会大点,一般到下午4点左右,状态会是一天中最好的,因为这时大部分的工作做得差不多了,又快要下班了,当然也不是绝对.要注意记录各下属 ...

  9. 【RocketMQ】消息的消费

    上一讲[RocketMQ]消息的拉取 消息消费 当RocketMQ进行消息消费的时候,是通过ConsumeMessageConcurrentlyService的submitConsumeRequest ...

随机推荐

  1. css权重计算规则

    1.第一等:代表内联样式,如 style=" ",权值为1000: 2.第二等:代表ID选择器,如 #content,权值为0100: 3.第三等:代表类,伪类和属性选择器,如 . ...

  2. 0703-spring cloud config-git配置属性加解密之对称加密

    一.概述 可以使用{cipher} *格式的加密值,只要有一个有效的密钥,那么它们将在主应用程序上下文获取环境之前被解密.要在应用程序中使用加密功能,您需要在您的类路径中包含Spring Securi ...

  3. 【分类器】感知机+线性回归+逻辑斯蒂回归+softmax回归

    一.感知机     详细参考:https://blog.csdn.net/wodeai1235/article/details/54755735 1.模型和图像: 2.数学定义推导和优化: 3.流程 ...

  4. Unity3D加密保护案例分享(一)

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏.建筑可视化.实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎.通过u ...

  5. javascript篇-typeof,instanceof,constructor,toString判断数据类型的用法和区别

    javascript基本数据类型有:string,number,Boolean,undefined,null 引用类型(复杂类型):object, ES6中新增了一种数据类型:Symbol 以上数据类 ...

  6. vue中的路由高亮

  7. rpm: /root/anaconda3/lib/liblzma.so.5: version `XZ_5.1.2alpha' not found (required by /lib64/librpmio.so.3)

    报如上的错误,发现rpm相关的命令都不能够用. 1.搜到这篇文章,https://stackoverflow.com/questions/47633870/rpm-lib64-liblzma-so-5 ...

  8. Python笔记:调用函数,带扩号和和不带括号的区别

    调用函数,如果带括号,那么是调用函数运行后的结果, 调用函数不带括号,调用的是函数本身 例如: def cun (a,b): return a+b print(cun) : 调用函数,打印的是函数 p ...

  9. Java创建文件夹

    import java.io.File; public class Mkdirs { public static void main(String[] args) { /** *创建文件夹,如果路径不 ...

  10. Facebook的bigpipe

    参考文档:英文版:http://www.cubrid.org/blog/dev-platform/faster-web-page-loading-with-facebook-bigpipe/ 搜索技术 ...