一. 生产者,根据某个标识将消息放到同一个队列中

在发送消息时,使用SelectMessageQueueByHash,该类根据传入进去的arg,进行hash计算,将消息分配到相应的队列中。

public class Producer {

    public static void main(String[] args) throws MQClientException {

        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
producer.setNamesrvAddr("10.130.41.36:9876");
producer.setInstanceName("Producer");
producer.setVipChannelEnabled(false);
producer.start(); String[] tags = {"tagA","tagB"}; for (int i = 1; i <= 10; i++) {
try {
Message msg = new Message("TopicTest",tags[i%tags.length],"key1"+i,("订单一号" + i).getBytes());
SendResult sendResult = producer.send(msg, new SelectMessageQueueByHash(),1);
System.out.println(sendResult);
} catch (Exception e) {
e.printStackTrace();
}
}
for (int i = 1; i <= 10; i++) {
try {
Message msg = new Message("TopicTest",tags[i%tags.length],"key2"+i,("订单二号" + i).getBytes());
SendResult sendResult = producer.send(msg, new SelectMessageQueueByHash(),2);
System.out.println(sendResult);
} catch (Exception e) {
e.printStackTrace();
}
}
for (int i = 1; i <= 10; i++) {
try {
Message msg = new Message("TopicTest",tags[i%tags.length],"key3"+i,("订单三号" + i).getBytes());
SendResult sendResult = producer.send(msg, new SelectMessageQueueByHash(),3);
System.out.println(sendResult);
} catch (Exception e) {
e.printStackTrace();
}
}
producer.shutdown();
}
}

  上述代码执行后Topic队列中的内容:

二. 消费者

(1). 顺序消费

使用MessageListenerOrderly,顺序消费同一个队列中的数据,只有第一个数据消费成功了才会消费第二个数据。

模拟在消费某个队列中的数据时出现了阻塞状态。

public class ConsumerOrderly {
public static void main(String[] args) throws InterruptedException,
MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName");
consumer.setNamesrvAddr("10.130.41.36:9876");
consumer.setInstanceName("Consumer1");
consumer.setMessageModel(MessageModel.CLUSTERING);
consumer.subscribe("TopicTest", "*"); consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
//设置自动提交,如果不设置自动提交就算返回SUCCESS,消费者关闭重启 还是会重复消费的
context.setAutoCommit(true);
try {
for (MessageExt msg:msgs) {
String msgKey = msg.getKeys();
if(msgKey.equals("key13") || msgKey.equals("key22")){
Thread.sleep(1000);
}
System.out.println(" 消费者1 ==> 当前线程:"+Thread.currentThread().getName()+" ,quenuID: "+msg.getQueueId()+ " ,content: " + new String(msg.getBody()));
}
} catch (Exception e) {
e.printStackTrace();
//如果出现异常,消费失败,挂起消费队列一会会,稍后继续消费
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
} //消费成功
return ConsumeOrderlyStatus.SUCCESS;
}
}); /**
* Consumer对象在使用之前必须要调用start初始化,初始化一次即可
*/
consumer.start(); System.out.println("C1 Started.");
}
}

  测试结果如下:

当"订单一号3"没有消费时 "订单一号4","订单一号5"是不能被消费的,"订单二号2"也是同样的情况。

(2). 并发消费

使用MessageListenerConcurrently,并发消费同一个队列中的数据,不能保证消费的顺序。

模拟在消费某个数据时出现了阻塞状态。

public class ConsumerConcurrently {
public static void main(String[] args) throws InterruptedException,
MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName");
consumer.setNamesrvAddr("10.130.41.36:9876");
consumer.setInstanceName("Consumer1");
consumer.setMessageModel(MessageModel.CLUSTERING); consumer.subscribe("TopicTest", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
try {
for (MessageExt msg:msgs) {
String msgKey = msg.getKeys();
if(msgKey.equals("key13") || msgKey.equals("key22")){
Thread.sleep(1000);
}
System.out.println(" 消费者1 ==> 当前线程:"+Thread.currentThread().getName()+" ,quenuID: "+msg.getQueueId()+ " ,content: " + new String(msg.getBody()));
}
} catch (Exception e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
} //消费成功
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.out.println("C1 Started.");
}
}

  测试结果如下

当消费"订单一号3"阻塞时,会将后面的数据交给其他线程消费,所以"订单一号4" ,"订单一号5"在 "订单一号3"之前消费了。

(3). 集群消费

不同消费者设置成相同的组名,在MessageModel.CLUSTERING模式下,不同消费者会消费不同的队列,同一个消费者中保证顺序

消费者1

public class ConsumerOrderly_1 {
public static void main(String[] args) throws InterruptedException,
MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName");
consumer.setNamesrvAddr("10.130.41.36:9876");
consumer.setInstanceName("Consumer1");
consumer.setMessageModel(MessageModel.CLUSTERING);
consumer.subscribe("TopicTest", "*"); consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
//设置自动提交,如果不设置自动提交就算返回SUCCESS,消费者关闭重启 还是会重复消费的
context.setAutoCommit(true);
try {
for (MessageExt msg:msgs) {
String msgKey = msg.getKeys();
if(msgKey.equals("key13")){
Thread.sleep(1000);
}
System.out.println(" 消费者1 ==> 当前线程:"+Thread.currentThread().getName()+" ,quenuID: "+msg.getQueueId()+ " ,content: " + new String(msg.getBody()));
}
} catch (Exception e) {
e.printStackTrace();
//如果出现异常,消费失败,挂起消费队列一会会,稍后继续消费
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
} //消费成功
return ConsumeOrderlyStatus.SUCCESS;
}
}); /**
* Consumer对象在使用之前必须要调用start初始化,初始化一次即可
*/
consumer.start(); System.out.println("C1 Started.");
}
}

消费者2

public class ConsumerOrderly_2 {
public static void main(String[] args) throws InterruptedException,
MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName");
consumer.setNamesrvAddr("10.130.41.36:9876");
consumer.setInstanceName("Consumer2");
consumer.setMessageModel(MessageModel.CLUSTERING);
consumer.subscribe("TopicTest", "*"); consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
//设置自动提交,如果不设置自动提交就算返回SUCCESS,消费者关闭重启 还是会重复消费的
context.setAutoCommit(true);
try {
for (MessageExt msg:msgs) {
String msgKey = msg.getKeys();
if(msgKey.equals("key22")){
Thread.sleep(1000);
}
System.out.println(" 消费者2 ==> 当前线程:"+Thread.currentThread().getName()+" ,quenuID: "+msg.getQueueId()+ " ,content: " + new String(msg.getBody()));
}
} catch (Exception e) {
e.printStackTrace();
//如果出现异常,消费失败,挂起消费队列一会会,稍后继续消费
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
} //消费成功
return ConsumeOrderlyStatus.SUCCESS;
}
}); /**
* Consumer对象在使用之前必须要调用start初始化,初始化一次即可
*/
consumer.start(); System.out.println("C2 Started.");
}
}

测试结果如下:

消费者1负责队列1,并保证队列1中的所有消息是按照顺序消费的

消费者2负责队列2和队列3,根据"订单二号2"可以看出,他保证了队列2和队列3的顺序消费。

(4). 消费者A和消费者B同组,消费者A消费tagA,消费者B消费tagB如图

在这种情况下,因为集群中订阅消息不一致,导致消费出现问题,最后启动的消费者才可以正常消费消息。

要解决这个问题,需要保证集群中的消费者拥有统一的订阅消息,Topic和Tag要一致才可以。

参考:
https://www.jianshu.com/p/524ef06ce25a
https://mp.weixin.qq.com/s/HbIS0yEJsCPMYwwYDBIvMQ

(5). 消费者A和消费者B不同组,消费者A消费tagA,消费者B消费tagB

在消费者1中,能保证tagA1,tagA2顺序的消费,消费者2中能保证tagB1,tagB2顺序的消费。
但是不能保证tagA1和tagB1的消费顺序。

测试代码:

消费者1

public class ConsumerOrderly_1 {
public static void main(String[] args) throws InterruptedException,
MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName");
consumer.setNamesrvAddr("10.130.41.36:9876");
consumer.setInstanceName("Consumer1");
consumer.setMessageModel(MessageModel.CLUSTERING);
consumer.subscribe("TopicTest", "tagA"); consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
//设置自动提交,如果不设置自动提交就算返回SUCCESS,消费者关闭重启 还是会重复消费的
context.setAutoCommit(true);
try {
for (MessageExt msg:msgs) {
System.out.println(" 消费者1 ==> 当前线程:"+Thread.currentThread().getName()+" ,quenuID: "+msg.getQueueId()+ " ,content: " + new String(msg.getBody()));
}
} catch (Exception e) {
e.printStackTrace();
//如果出现异常,消费失败,挂起消费队列一会会,稍后继续消费
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
} //消费成功
return ConsumeOrderlyStatus.SUCCESS;
}
}); /**
* Consumer对象在使用之前必须要调用start初始化,初始化一次即可
*/
consumer.start(); System.out.println("C1 Started.");
}
}

消费者2

public class ConsumerOrderly_2 {
public static void main(String[] args) throws InterruptedException,
MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName1");
consumer.setNamesrvAddr("10.130.41.36:9876");
consumer.setInstanceName("Consumer2");
consumer.setMessageModel(MessageModel.CLUSTERING);
consumer.subscribe("TopicTest", "tagB"); consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
//设置自动提交,如果不设置自动提交就算返回SUCCESS,消费者关闭重启 还是会重复消费的
context.setAutoCommit(true);
try {
for (MessageExt msg:msgs) {
String msgKey = msg.getKeys();
if(msgKey.equals("key11")){
Thread.sleep(1000);
}
System.out.println(" 消费者2 ==> 当前线程:"+Thread.currentThread().getName()+" ,quenuID: "+msg.getQueueId()+ " ,content: " + new String(msg.getBody()));
}
} catch (Exception e) {
e.printStackTrace();
//如果出现异常,消费失败,挂起消费队列一会会,稍后继续消费
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
} //消费成功
return ConsumeOrderlyStatus.SUCCESS;
}
}); /**
* Consumer对象在使用之前必须要调用start初始化,初始化一次即可
*/
consumer.start(); System.out.println("C2 Started.");
}
}

测试结果:

消费者1

消费者2

"订单一号2" 在 "订单一号1" 前被消费了。

RocketMQ生产消费模型选择的更多相关文章

  1. Python并发编程-生产消费模型

    生产消费模型初步 #产生两个子进程,Queue可以在子进程之间传递消息 from multiprocessing import Queue,Process import random import t ...

  2. Python学习之路并发编程--信号量、事件、队列及生产消费模型

    1. 信号量 对于多进程来说,多个进程同时修改数据,就可能出现安全隐患,所以引入了锁,这一机制,但锁只能有一把来控制一个的开关,当你需要几把锁的时候,就可能用到信号量的概念.他是用了锁的原理,内置了一 ...

  3. 初识Kafka:构架、生产消费模型以及其他相关概念

    当前使用的事件总线采用的是Kafka分布式消息队列来完成的,近来项目需要接入到事件总线中,故开启了kafka的学习之旅(之前一直在听说kafka这玩意儿,但是学习计划中还没有将它安排进去,借着这个机会 ...

  4. 并发编程:生产消费模型、死锁与Rlock、线程、守护线程、信号量、锁

    一.生产者消费者模型1 二.生产者消费者模型2 三.守护线程 四.常用方法 五.启动线程的另一种方式 六.锁 七.锁死 八.死锁 九.单个锁能不能死锁 十.信号旗 一.生产者消费者模型1 import ...

  5. Day9 进程同步锁 进程队列 进程池 生产消费模型 进程池 paramike模块

    进程同步锁: 当运行程序的时候,有可能你的程序同时开多个进程,开进程的时候会将多个执行结果打印出来,这样的话打印的信息都是错乱的,怎么保证打印信息是有序的呢? 其实也就是相当于让进程独享资源. fro ...

  6. JAVA 多线程制作大球吃小球 一、实现球的自动生成及运动 生产消费模型

    前几天用多线程实现了创建小球并移动,想到大鱼吃小鱼,便突发奇想要写一个大球吃小球.首先第一步自然是先把界面弄好啦 public class BallUI extends JPanel { privat ...

  7. 并发编程: 生产消费模型、死锁与Rlock、线程、守护线程、信号量、锁

    一.守护进程 二.互斥锁 三.抢票 四.进程间通讯 五.进程间通讯2 一.守护进程 """ 进程间通讯的另一种方式 使用queue queue 队列 队列的特点: 先进的 ...

  8. 使用wait/notify实现生产消费模型

    public class A { private Deque<Integer> list = new LinkedList<>(); private int max = 10; ...

  9. Linux——多线程下解决生产消费者模型

    我们学习了操作系统,想必对生产消费者问题都不陌生.作为同步互斥问题的一个经典案例,生产消费者模型其实是解决实际问题的基础模型,解决很多的实际问题都会依赖于它.而此模型要解决最大的问题便是同步与互斥.而 ...

随机推荐

  1. HttpReponse

    属性: django将请求报文中的请求行.头部信息.内容主体封装成 HttpRequest 类中的属性.   除了特殊说明的之外,其他均为只读的. 0.HttpRequest.scheme   表示请 ...

  2. 20165230 Exp3 免杀原理与实践

    目录 1.实验内容 2.基础问题回答 3.实验内容 任务一:正确使用免杀工具或技巧 使用msf编码器,msfvenom生成如jar之类的其他文件 使用veil-evasion 自己利用shellcod ...

  3. k64 datasheet学习笔记22---Direct Memory Access Controller (eDMA)

    0.前言 本文主要介绍DMA相关内容 1.简介 DMA模块包含: 1.一个DMA引擎 源和目的地址的计算 数据搬移 2.本地存储的传输控制描述TCD,对于16个传输通道中的每一个各对应一个TCD 1. ...

  4. Charles 抓包的简单使用

    1.准备工具: 软件 Charles 手机 随意哪个现代手机 2.基本配置 安装Charles的电脑和手机在同一个局域网下, 点击手机上的和电脑练得同一个局域网的名字进行配置,里面有个代理,选择手动, ...

  5. 【原创】大数据基础之Zookeeper(3)选举算法

    提到zookeeper选举算法,就不得不提Paxos算法,因为zookeeper选举算法是Paxos算法的一个变种: Paxos要解决的问题是:在一个分布式网络环境中有众多的参与者,但是每个参与者都不 ...

  6. @ApiModelProperty的用法

    @ApiModelProperty()用于方法,字段: 表示对model属性的说明或者数据操作更改 value–字段说明 name–重写属性名字 dataType–重写属性类型 required–是否 ...

  7. 巧用Win+R

    calc 启动计算器 charmap 启动字符映射表 chkdsk Chkdsk磁盘检查 cleanmgr 磁盘清理 clipbrd 剪贴板查看器 cmd CMD命令提示符 dvdplay DVD播放 ...

  8. 洛谷P3398 仓鼠找suger

    传送门啦 思路: 那么从 $ A $ 到 $ B $ 可以分解成 先从 $ A $ 到 $ X $ 再从 $ X $ 到 $ B $ ... 另一个同理 假设能相遇 那么 要么在 $ A $ 到 $ ...

  9. golang mysql 的 packet sequence error 这个错

    在公司用golang 写了个插入外链数据的服务,这服务是2016年写的,大概作用就是,python 爬取的数据,要同步到 wordpress中,golang就负责,将数据整理,图片下载弄到 wordp ...

  10. Spring AOP概念理解

    1.我所知道的aop 初看aop,上来就是一大堆术语,而且还有个拉风的名字,面向切面编程,都说是OOP的一种有益补充等等.一下子让你不知所措,心想着:怪不得很多人都和我说aop多难多难.当我看进去以后 ...