RocketMQ—RocketMQ发送同步、异步、单向、延迟、批量、顺序、批量消息、带标签消息

发送同步消息

生产者发送消息,mq进行确认,然后返回给生产者状态。这就是同步消息。

前文demo程序就是发送的同步消息。

发送异步消息

异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应。发送完以后会有一个异步消息通知。

代码如下:

/**
* 异步消息测试
*/
@Test
public void simpleAsyncProducer() throws Exception {
//创建一个生产者,并指定一个组名
DefaultMQProducer producer = new DefaultMQProducer("async-producer-group");
//连接namesrv,参数是namesrv的ip地址:端口号
producer.setNamesrvAddr(MqConstant.NAME_SERVER_ADDR);
//启动
producer.start(); //指定topic,创建一个消息
Message message = new Message("asyncTopic1", "这是一条异步消息".getBytes()); //发送异步消息,并设置回调内容
producer.send(message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
log.info("回调内容,发送成功");
} @Override
public void onException(Throwable throwable) {
log.info("回调内容,发送失败");
}
}); log.info("主线程执行中========="); System.in.read();
}

从运行结果可以看到是不同的线程输出的内容。

发送单向消息

这种方式主要用在不关心发送结果的场景,这种方式吞吐量很大,但是存在消息丢失的风险,例如日志信息的发送。

代码如下:

@Test
public void oneWayMessageTest() throws Exception {
//创建一个生产者,并指定一个组名
DefaultMQProducer producer = new DefaultMQProducer("oneway-producer-group");
//连接namesrv,参数是namesrv的ip地址:端口号
producer.setNamesrvAddr(MqConstant.NAME_SERVER_ADDR);
//启动
producer.start(); //指定topic,创建一个消息
Message message = new Message("onewayTopic1", "这是一条单向消息".getBytes()); //发送单向消息
producer.sendOneway(message); producer.shutdown();
}

发送延迟消息

消息放入mq后,过一段时间,才会被监听到,然后消费.

比如下订单业务,提交了一个订单就可以发送一个延时消息,30min后去检查这个订单的状态,如果还是未付款就取消订单释放库存。

代码如下

@Test
public void msMessageTest() throws Exception{
//创建一个生产者,并指定一个组名
DefaultMQProducer producer = new DefaultMQProducer("ms-producer-group");
//连接namesrv,参数是namesrv的ip地址:端口号
producer.setNamesrvAddr(MqConstant.NAME_SERVER_ADDR);
//启动
producer.start(); //指定topic,创建一个消息
Message message = new Message("msTopic1", "这是一条单向消息".getBytes());
//给消息设置一个延迟时间
message.setDelayTimeLevel(3); //发送延时消息
producer.sendOneway(message); producer.shutdown();
}

延时等级如下:

发送批量消息

代码如下:

@Test
public void testBatchProducer() throws Exception {
// 创建默认的生产者
DefaultMQProducer producer = new DefaultMQProducer("test-batch-group");
//连接namesrv,参数是namesrv的ip地址:端口号
producer.setNamesrvAddr(MqConstant.NAME_SERVER_ADDR);
// 启动实例
producer.start();
List<Message> msgs = Arrays.asList(
new Message("batchTopicTest", "我是一组消息的A消息".getBytes()),
new Message("batchTopicTest", "我是一组消息的B消息".getBytes()),
new Message("batchTopicTest", "我是一组消息的C消息".getBytes()) );
SendResult send = producer.send(msgs);
System.out.println(send);
// 关闭实例
producer.shutdown();
}

这些消息会被放到同一个队列中。

发送顺序消息

可以想象一个场景,我们在网上购物时,需要先完成下订单操作,然后再去发短信,再进行发货,需要保证顺序的。

前文我们讲的都是并发消息,这种消息并不能完成上述的场景逻辑。比如一个topic里有10个消息,分别在4个队列中;

  • 如果消费者,同时有20个线程在消费,可能A线程拿到消息1了,B线程拿到消息2了,但是B线程可能完成的比A线程早,这就没办法上述场景的顺序了。
  • 如果消费者只有一个线程,轮询消费四个队列中的消息时,也不能保证是网购场景中的顺序的。

这就要引出顺序消息:把消费者变为单线程,把下订单消息、发短信消息、发货消息放到同一个队列就可以了。

代码

消息封装成实体类如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MessageModel {
//订单id
private String orderId;
//用户id
private String userId;
//消息描述
private String description;
}

发送顺序消息的生产者代码如下:

/**
* 顺序消息
*/ @Test
public void testOrderlyProducer() throws Exception { List<MessageModel> messageModelList = Arrays.asList(
//用户1的订单
new MessageModel("order-111","user-1","下单"),
new MessageModel("order-111","user-1","发短信"),
new MessageModel("order-111","user-1","发货"), //用户2的订单
new MessageModel("order-222","user-2","下单"),
new MessageModel("order-222","user-2","发短信"),
new MessageModel("order-222","user-2","发货")
); // 创建默认的生产者
DefaultMQProducer producer = new DefaultMQProducer("test-orderly-group");
//连接namesrv,参数是namesrv的ip地址:端口号
producer.setNamesrvAddr(MqConstant.NAME_SERVER_ADDR);
// 启动实例
producer.start(); //发送顺序消息时 发送时相同用户的消息要保证有序,并且发到同一个队列里
messageModelList.forEach(
messageModel->{
Message message = new Message("orderlyTopic", messageModel.toString().getBytes());
try {
//发送消息,相同订单号去相同队列
producer.send(message, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message message, Object arg) {
//producer.send(message,selector,arg),第三个参数订单号会传给selector要实现的方法的arg
//在这里选择队列
int hashCode = Math.abs(arg.toString().hashCode());
int index = hashCode % mqs.size();
return mqs.get(index);
}
}, messageModel.getOrderId());
} catch (Exception e) {
log.error("有错误发生",e);
}
}
);
// 关闭实例
producer.shutdown();
log.info("发送完成");
}

消费顺序消息的消费者代码如下:

//消费者
@Test
public void orderlyConsumer() throws Exception {
//创建一个消费者,并指定一个组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test-orderly-consumer-group");
//连接namesrv,参数是namesrv的ip地址:端口号
consumer.setNamesrvAddr(MqConstant.NAME_SERVER_ADDR);
//订阅一个主题 *号表示订阅这个主题中所有的消息
consumer.subscribe("orderlyTopic","*");
//设置一个监听器(一直监听,异步回调方式)
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
log.info("线程id"+Thread.currentThread().getId());
log.info("消息内容:"+new String(msgs.get(0).getBody()));
return ConsumeOrderlyStatus.SUCCESS;
}
}); //启动消费者
consumer.start(); //挂起当前jvm,防止主线程结束,让监听器一直监听
System.in.read(); }

运行结果如下:

可以看到同一个订单是顺序消费的。

其他问题

如果我们的消息消费失败了怎么办?

如果是并发模式,消费失败会进行重试,重试16次后还会没消费成功,会被放到死信队列里。

如果是顺序模式,如果重试失败,会无限重试,是int的最大值。

发送带标签的消息,消息过滤

如果我们有衣服订单的消息、手机订单的消息,如果我们只使用topic进行区分,就要使用两个topic;但是它们都是订单,所以在同一个topic中会好一些,Rocketmq就提供了消息过滤功能,通过tag或者key进行区分。

生产者代码如下:

@Test
public void testTagProducer() throws Exception {
// 创建默认的生产者
DefaultMQProducer producer = new DefaultMQProducer("test-tag-group");
//连接namesrv,参数是namesrv的ip地址:端口号
producer.setNamesrvAddr(MqConstant.NAME_SERVER_ADDR);
// 启动实例
producer.start();
Message messageTopic1 = new Message("tagTopic", "tag1", "这是topic1的消息".getBytes());
Message messageTopic2 = new Message("tagTopic", "tag2", "这是topic2的消息".getBytes());
producer.send(messageTopic1);
producer.send(messageTopic2);
// 关闭实例
producer.shutdown();
}

消费tag1的消费者

//消费tag1的消费者
@Test
public void tagConsumer1() throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("tag-consumer-group-a");
consumer.setNamesrvAddr(MqConstant.NAME_SERVER_ADDR);
consumer.subscribe("tagTopic", "tag1");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
System.out.println("我是tag1的消费者,我正在消费消息" + new String(msgs.get(0).getBody()));
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.in.read();
}

消费tag1和tag2的消费者

//消费tag1和tag2的消费者
@Test
public void tagConsumer2() throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("tag-consumer-group-a");
consumer.setNamesrvAddr(MqConstant.NAME_SERVER_ADDR);
consumer.subscribe("tagTopic", "tag1 || tag2");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
System.out.println("我是tag1和tag2的消费者,我正在消费消息" + new String(msgs.get(0).getBody()));
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.in.read();
}

带key的消息

消息都会有自己的MessageId的,如下图:

那我们能否指定id呢?

在发送消息时可以指定key:

@Test
public void testKeyProducer() throws Exception {
// 创建默认的生产者
DefaultMQProducer producer = new DefaultMQProducer("test-key-group");
//连接namesrv,参数是namesrv的ip地址:端口号
producer.setNamesrvAddr(MqConstant.NAME_SERVER_ADDR); String key = UUID.randomUUID().toString(); // 启动实例
producer.start();
Message messageTopic1 = new Message("keyTopic", "tag1",key, "这是topic1的消息".getBytes());
producer.send(messageTopic1);
// 关闭实例
producer.shutdown();
}

消费者获取key:

@Test
public void testKeyConsumer() throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("key-consumer-group-a");
consumer.setNamesrvAddr(MqConstant.NAME_SERVER_ADDR);
consumer.subscribe("keyTopic","*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
System.out.println("我们设置的key:" + msgs.get(0).getKeys());
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.in.read();
}

输出如下:

RocketMQ—RocketMQ发送同步、异步、单向、延迟、批量、顺序、批量消息、带标签消息的更多相关文章

  1. RocketMQ的发送模式和消费模式

    前言 小伙伴们大家好啊,王子又来和大家一起闲谈MQ技术了. 通过之前文章的学习,我们已经对RocketMQ的基本架构有了初步的了解,那今天王子就和大家一起来点实际的,用代码和大家一起看看RocketM ...

  2. 哪5种IO模型?什么是select/poll/epoll?同步异步阻塞非阻塞有啥区别?全在这讲明白了!

    系统中有哪5种IO模型?什么是 select/poll/epoll?同步异步阻塞非阻塞有啥区别? 本文地址http://yangjianyong.cn/?p=84转载无需经过作者本人授权 先解开第一个 ...

  3. 【RocketMQ源码学习】- 3. Client 发送同步消息

    本文较长,代码后面给了方法简图,希望给你帮助 发送的方式 同步发送 异步发送 消息的类型 普通消息 顺序消息 事务消息 发送同步消息的时序图 为了防止读者朋友嫌烦,可以看下时序图,后面我也会给出方法的 ...

  4. RocketMQ(6)---发送普通消息(三种方式)

    发送普通消息(三种方式) RocketMQ 发送普通消息有三种实现方式:可靠同步发送.可靠异步发送.单向(Oneway)发送. 注意 :顺序消息只支持可靠同步发送. GitHub地址: https:/ ...

  5. RabbitMQ,RocketMQ,Kafka 事务性,消息丢失和消息重复发送的处理策略

    消息队列常见问题处理 分布式事务 什么是分布式事务 常见的分布式事务解决方案 基于 MQ 实现的分布式事务 本地消息表-最终一致性 MQ事务-最终一致性 RocketMQ中如何处理事务 Kafka中如 ...

  6. Spring boot实战项目整合阿里云RocketMQ (非开源版)消息队列实现发送普通消息,延时消息 --附代码

    一.为什么选择RocketMQ消息队列? 首先RocketMQ是阿里巴巴自研出来的,也已开源.其性能和稳定性从双11就能看出来,借用阿里的一句官方介绍:历年双 11 购物狂欢节零点千万级 TPS.万亿 ...

  7. 【RocketMQ】主从同步实现原理

    主从同步的实现逻辑主要在HAService中,在DefaultMessageStore的构造函数中,对HAService进行了实例化,并在start方法中,启动了HAService: public c ...

  8. ActiveMQ producer同步/异步发送消息

    http://activemq.apache.org/async-sends.html producer发送消息有同步和异步两种模式,可以通过代码配置: ((ActiveMQConnection)co ...

  9. iOS使用NSURLConnection发送同步和异步HTTP Request

    1. 同步发送 - (NSString *)sendRequestSync { // 初始化请求, 这里是变长的, 方便扩展 NSMutableURLRequest *request = [[NSMu ...

  10. 深入研究RocketMQ生产者发送消息的底层原理

    前言 hello,小伙伴们,王子又来和大家研究RocketMQ的原理了,之前的文章RocketMQ生产部署架构如何设计中,我们已经简单的聊过了生产者是如何发送消息给Broker的. 我们简单回顾一下这 ...

随机推荐

  1. 阿里云 Serverless Kubernetes 的落地实践分享

    作者 | 元毅(阿里云容器平台高级开发工程师) ​ 微信搜索关注 Serverless 公众号,后台回复 深圳 可获取本文 PPT 导读** Kubernetes 作为当今云原生业界标准,具备良好的生 ...

  2. vue 状态管理 三、Mutations和Getters用法

    系列导航 vue 状态管理 一.状态管理概念和基本结构 vue 状态管理 二.状态管理的基本使用 vue 状态管理 三.Mutations和Getters用法 vue 状态管理 四.Action用法 ...

  3. d3生成器--line,area,diagonal

    https://blog.csdn.net/qq_31396185/article/details/78147612

  4. Java 如何将Excel转换为TXT文本格式

    TXT文件是一种非常简单.通用且易于处理的文本格式.在处理大规模数据时,将Excel转为TXT纯文本文件可以提高处理效率.此外,许多编程语言和数据处理工具都有内置的函数和库来读取和处理TXT文件,因此 ...

  5. 基于python+django的家教预约网站-家教信息管理系统设计与实现

    该系统是基于python+django开发的家教预约网站.是给师妹做的课程作业.大家在学习过程中,遇到问题可以在github给作者留言. 效果演示 前台地址: http://jiajiao.gitap ...

  6. 【面试题精讲】JVM中有哪些垃圾收集器

    有时博客内容会有变动,首发博客是最新的,其他博客地址可能未同步,请认准https://blog.zysicyj.top 首发博客地址 系列文章地址 在Java虚拟机(JVM)中,有以下几种常见的垃圾收 ...

  7. Redis异常问题分析黄金一分钟

    Redis异常问题分析黄金一分钟 背景 同事发现一个环境redis比较卡顿,导致业务比较难以开展. 问题是下午出现的. 六点左右找到我这边. 想着帮忙看看, 问题其实没有定位完全, 仅是发现了一个可能 ...

  8. 鲲鹏920上面 Docker 部署 clickhouse 的方式方法

    鲲鹏920上面 Docker 部署 clickhouse 的方式方法 背景 最近有一套鲲鹏920的测试环境, 研发同事想纯Dcoker部署一套环境. 其中就包括了 Clickhouse 之前发现Cli ...

  9. [转帖]Java 8 要过时了?从JDK8飞升到JDK17,一次性给你讲明白

    https://blog.csdn.net/agonie201218/article/details/127916729?spm=1001.2101.3001.6650.2&utm_mediu ...

  10. [转帖]Oracle数据库开启NUMA支持

    NUMA简介 NUMA(Non Uniform Memory Access Architecture,非统一内存访问)把一台计算机分成多个节点(node),每个节点内部拥有多个CPU,节点内部使用共有 ...