一、部署单点RocketMQ
Docker 部署 RocketMQ (图文并茂超详细)_docker 部署rocketmq-CSDN博客
这个博主讲的很好,可食用,替大家实践了一遍
二、原理篇
为什么使用RocketMQ:
关于一些原理,感觉官网讲的也非常透彻
还有一些功能特性:
本文的实操篇只是讲了发送普通消息
关于中间件对比,下面我之前有看过一些很好的文章:
Kafka、RabbitMQ、RocketMQ等消息中间件的对比_rabbimq rocket 差异-CSDN博客
rpc和zmq性能对比 rpc mq区别_mob6454cc70642f的技术博客_51CTO博客
RabbitMQ,RocketMQ,Kafka--区别/对比/选型_51CTO博客_rocketmq rabbitmq kafka选型
三、实操篇
先讲讲原理:
如果你需要不同业务,就需要不同消费者组,不要想着同一个消费者组可以通过订阅不同主题达到不同业务,因为同一个消费者组内的功能必须是一致的,可以换个角度想,既然你是一个业务,一个业务就是一个主题嘛,你用不同的业务实现,就多添加几个消费者组,分别订阅那个主题(业务),然后通过不同的Tag区分就行了,而且而且,不要想着说:一个消费者组一个主题通过不同Tag来区分,虽然我在刚刚学习的时候也这样子想,结果踩了一天的坑,看了好多博客好文来理解,在文末也有关于为什么不能这样子做。
1、引入依赖
RocketMQ的依赖:
XML
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.2.3</version>
</dependency>
demo案例的全部依赖:
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bluefoxyu</groupId>
<artifactId>RocketMQ-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.27</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.16</version>
</dependency>
</dependencies>
</project>
2、启动自动装配
2.2.3 版本的RocketMQ 没有适配 SpringBoot3,只适配SpringBoot2,所以需要自己去配置好自动装配。可以参考我下面这篇文章:
Springboot3+自动装配_springboot3自动装配-CSDN博客
在项目中的 resources 目录下创建 META-INF/spring 文件夹,并创建下面这个文件。
XML
org.springframework.boot.autoconfigure.AutoConfiguration.imports
XML
# RocketMQ 2.2.3 version does not adapt to SpringBoot3
org.apache.rocketmq.spring.autoconfigure.RocketMQAutoConfiguration
3、配置application.yml
XML
server:
port: 8080
spring:
profiles:
active: dev
rocketmq:
name-server: xxx:9876 # NameServer 地址
producer:
group: rocketmq-v3-demo-sent-message-group_bluefoxyu # 全局发送者组定义
send-message-timeout: 2000
# 发送消息失败时的重试次数。设置为 1 表示如果发送失败,会再重试一次(总共尝试两次)。适用于同步发送消息失败时的重试次数。
retry-times-when-send-failed: 1
# 异步发送失败时的重试次数。设置为 1 表示在异步发送失败时会再尝试一次。适用于异步发送消息失败时的重试次数。
retry-times-when-send-async-failed: 1
logging:
level:
com:
bluefoxyu:
producer: info
consumer: info
controller: info
4、启动类
相比这个不必多说了。
RocketMQApplication:
java
package com.bluefoxyu;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Slf4j
@SpringBootApplication
public class RocketMQApplication {
public static void main(String[] args) {
SpringApplication.run(RocketMQApplication.class, args);
}
}
5、编写一个统一格式的消息对象
java
package com.bluefoxyu.message;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GeneralMessageEvent implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String body;
private String keys;
}
上述实体类实现了Serializable接口,能够正常被序列化或者反序列化。
6、生产者
编写一个生产者,统一做好发送消息的一个模板,方便简化接口实现发送消息的代码编写,显得更加优雅一点,说到发送消息,就需要知道发送到哪个主题,然后哪些消费者组去消费,然后还有每条消息的唯一标识key,唯一标识可以用uuid生成,也可以用redis生成一个增长的不重复的id,这里使用uuid简化。
注意:如果你的项目里面只有一个消费者组,只有一个消费业务,这样子是不需要传Tag(过滤标签)的,但是正常情况都会有多个消息队列任务,下面提供两种重载的方法。
code:
java
package com.bluefoxyu.producer;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.bluefoxyu.message.GeneralMessageEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.MessageConst;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
/**
* 封装全体的消息生产者
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class GeneralMessageProducer {
private final RocketMQTemplate rocketMQTemplate;
/**
* 发送普通消息
*
* @param topic 消息发送主题,用于标识同一类业务逻辑的消息
* @param keys 消息索引键,可根据关键字精确查找某条消息
* @param messageSendEvent 普通消息发送事件,自定义对象,最终都会序列化为字符串
* @return 消息发送 RocketMQ 返回结果
*/
public SendResult sendMessage(String topic, String keys, GeneralMessageEvent messageSendEvent) {
SendResult sendResult;
try {
Message<?> message = MessageBuilder
.withPayload(messageSendEvent)
.setHeader(MessageConst.PROPERTY_KEYS, keys)
.build();
// 2000L 表示发送消息的超时时间为 2000 毫秒,即 2 秒
sendResult = rocketMQTemplate.syncSend(
topic,
message,
2000L
);
log.info("[普通消息] 消息发送结果:{},消息ID:{},消息Keys:{}", sendResult.getSendStatus(), sendResult.getMsgId(), keys);
} catch (Throwable ex) {
log.error("[普通消息] 消息发送失败,消息体:{}", JSON.toJSONString(messageSendEvent), ex);
throw ex;
}
return sendResult;
}
/**
* 发送普通消息
*
* @param topic 消息发送主题,用于标识同一类业务逻辑的消息
* @param tag 消息的过滤标签,消费者可通过Tag对消息进行过滤,仅接收指定标签的消息。
* @param keys 消息索引键,可根据关键字精确查找某条消息
* @param messageSendEvent 普通消息发送事件,自定义对象,最终都会序列化为字符串
* @return 消息发送 RocketMQ 返回结果
*/
public SendResult sendMessage(String topic, String tag, String keys, GeneralMessageEvent messageSendEvent) {
SendResult sendResult;
try {
// 构建消息的 destination (主题和标签)
StringBuilder destinationBuilder = StrUtil.builder().append(topic);
if (StrUtil.isNotBlank(tag)) {
destinationBuilder.append(":").append(tag); // 设置tag
}
Message<?> message = MessageBuilder
.withPayload(messageSendEvent)
.setHeader(MessageConst.PROPERTY_KEYS, keys)
.setHeader(MessageConst.PROPERTY_TAGS, tag) // 设置消息的标签
.build();
// 2000L 表示发送消息的超时时间为 2000 毫秒,即 2 秒
sendResult = rocketMQTemplate.syncSend(
destinationBuilder.toString(),
message,
2000L
);
log.info("[普通消息] 消息发送结果:{},消息ID:{},消息Keys:{}", sendResult.getSendStatus(), sendResult.getMsgId(), keys);
} catch (Throwable ex) {
log.error("[普通消息] 消息发送失败,消息体:{}", JSON.toJSONString(messageSendEvent), ex);
throw ex;
}
return sendResult;
}
}
7、定义一个constant
java
package com.bluefoxyu.constant;
/**
* RocketMQ 常量类
* @author bluefoxyu
*/
public class RocketMQConstant {
/**
* Group 消费者组定义
*/
public static final String GENERAL_MESSAGE_CONSUMER_GROUP = "general_message_consumer_group";
public static final String MESSAGE_CONSUMER_GROUP_A = "message_consumer_group_A";
public static final String MESSAGE_CONSUMER_GROUP_B = "message_consumer_group_B";
public static final String MESSAGE_CONSUMER_GROUP_C = "message_consumer_group_C";
/**
* Topic 主题定义
*/
public static final String MESSAGE_TOPIC_1 = "message_topic_1";
public static final String MESSAGE_TOPIC_2 = "message_topic_2";
/**
* Tag 标签
*/
public static final String MESSAGE_TAG_A = "message_tag_A";
public static final String MESSAGE_TAG_B = "message_tag_B";
public static final String MESSAGE_TAG_C = "message_tag_C";
}
8、多/单个消费者订阅一个主题
1.实现消费者
这里需要实现监听的消息的实体类类型是什么,GeneralMessageEvent 是我们之前封装的统一消息对象
implements RocketMQListener<GeneralMessageEvent>
在onMessage方法中,通过
JSON.toJSONString(message)
就可以拿到解析好的消息内容,也就是我们真正需要发送的消息,下面我编写三个消费者来进行消费,不过绑定的都是同一个主题,类似负载均衡的功能,这里只用一个消费者也是一样的,因为后续还需要测其他功能,所以这里我写了三个消费者。
GeneralMessageConsumer1:
java
package com.bluefoxyu.consumer;
import com.alibaba.fastjson.JSON;
import com.bluefoxyu.message.GeneralMessageEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
import static com.bluefoxyu.constant.RocketMQConstant.GENERAL_MESSAGE_CONSUMER_GROUP;
import static com.bluefoxyu.constant.RocketMQConstant.MESSAGE_TOPIC_1;
/**
* topic 对应主题
* consumerGroup 指定消费的分组
*/
@Slf4j
@Component
@RocketMQMessageListener(
topic = MESSAGE_TOPIC_1,
consumerGroup = GENERAL_MESSAGE_CONSUMER_GROUP // 相同的 consumerGroup
)
public class GeneralMessageConsumer1 implements RocketMQListener<GeneralMessageEvent>{
@Override
public void onMessage(GeneralMessageEvent message) {
log.info("[消费者GeneralMessageConsumer1] 接收到消息,消息体:{},消息:{}", JSON.toJSONString(message), JSON.toJSONString(message.getBody()));
}
}
GeneralMessageConsumer2:
java
package com.bluefoxyu.consumer;
import com.alibaba.fastjson.JSON;
import com.bluefoxyu.message.GeneralMessageEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
import static com.bluefoxyu.constant.RocketMQConstant.GENERAL_MESSAGE_CONSUMER_GROUP;
import static com.bluefoxyu.constant.RocketMQConstant.MESSAGE_TOPIC_1;
/**
* topic 对应主题
* consumerGroup 指定消费的分组
*/
@Slf4j
@Component
@RocketMQMessageListener(
topic = MESSAGE_TOPIC_1,
consumerGroup = GENERAL_MESSAGE_CONSUMER_GROUP // 相同的 consumerGroup
)
public class GeneralMessageConsumer2 implements RocketMQListener<GeneralMessageEvent>{
@Override
public void onMessage(GeneralMessageEvent message) {
log.info("[消费者GeneralMessageConsumer2] 接收到消息,消息体:{},消息:{}", JSON.toJSONString(message), JSON.toJSONString(message.getBody()));
}
}
GeneralMessageConsumer3:
java
package com.bluefoxyu.consumer;
import com.alibaba.fastjson.JSON;
import com.bluefoxyu.message.GeneralMessageEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
import static com.bluefoxyu.constant.RocketMQConstant.GENERAL_MESSAGE_CONSUMER_GROUP;
import static com.bluefoxyu.constant.RocketMQConstant.MESSAGE_TOPIC_1;
/**
* topic 对应主题
* consumerGroup 指定消费的分组
*/
@Slf4j
@Component
@RocketMQMessageListener(
topic = MESSAGE_TOPIC_1,
consumerGroup = GENERAL_MESSAGE_CONSUMER_GROUP // 相同的 consumerGroup
)
public class GeneralMessageConsumer3 implements RocketMQListener<GeneralMessageEvent> {
@Override
public void onMessage(GeneralMessageEvent message) {
log.info("[消费者GeneralMessageConsumer3] 接收到消息,消息体:{},消息:{}", JSON.toJSONString(message), JSON.toJSONString(message.getBody()));
}
}
2.编写接口发送消息
发送消息需要发送这四要素:
- topic 主题
- key 唯一标识
- message 需要发送的消息
java
package com.bluefoxyu.controller;
import com.bluefoxyu.producer.GeneralMessageProducer;
import com.bluefoxyu.message.GeneralMessageEvent;
import lombok.RequiredArgsConstructor;
import org.apache.rocketmq.client.producer.SendResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import static com.bluefoxyu.constant.RocketMQConstant.MESSAGE_TOPIC_1;
@RestController
@RequiredArgsConstructor
public class controller {
private final GeneralMessageProducer generalMessageDemoProduce;
@PostMapping("/send/topic1/general-messageA")
public String sendTopic1GeneralMessageA() {
String keys = UUID.randomUUID().toString();
GeneralMessageEvent generalMessageEvent = GeneralMessageEvent.builder()
.body("Message for A")
.keys(keys)
.build();
SendResult sendResult = generalMessageDemoProduce.sendMessage(
MESSAGE_TOPIC_1,
keys,
generalMessageEvent
);
// 返回发送成功的状态名
return sendResult.getSendStatus().name();
}
@PostMapping("/send/topic1/general-messageB")
public String sendTopic1GeneralMessageB() {
String keys = UUID.randomUUID().toString();
GeneralMessageEvent generalMessageEvent = GeneralMessageEvent.builder()
.body("Message for B")
.keys(keys)
.build();
SendResult sendResult = generalMessageDemoProduce.sendMessage(
MESSAGE_TOPIC_1,
keys,
generalMessageEvent
);
return sendResult.getSendStatus().name();
}
@PostMapping("/send/topic1/general-messageC")
public String sendTopic1GeneralMessageC() {
String keys = UUID.randomUUID().toString();
GeneralMessageEvent generalMessageEvent = GeneralMessageEvent.builder()
.body("Message for C")
.keys(keys)
.build();
SendResult sendResult = generalMessageDemoProduce.sendMessage(
MESSAGE_TOPIC_1,
keys,
generalMessageEvent
);
return sendResult.getSendStatus().name();
}
}
3.接口测试
准备这三个测试接口:
开始分别测试三个接口这里就不一一展示了。
看控制台:
如结果消费了三次
9、测试多个消费者分别订阅不同主题
如果相同消费组的三个消费者组分别订阅不同主题,会怎么样呢。修改的代码如下,
当然,哈哈哈哈哈哈,就是消费不到消息(对于小白的我也被困扰了好久),由于是有问题的,代码就不粘贴了【狗头】。如下:
这里参考了一篇大佬的文章:
rocketmq问题汇总-一个consumerGroup只对应一个topic_org.apache.rocketmq.client.exception.mqbrokerexcep-CSDN博客
看完后悟了很多,大概意思就是一个消费者组中的职责应该是一致的,应该都去订阅相同主题的,如果一个消费者订阅了两个主题,那么其他同组的消费者也应该订阅那两个主题,参考评论区这几大佬的评论:
这个大佬就说的很透彻了:
10、一个消费者订阅多个主题
在上面说了既然一个消费者可以订阅多个主题,但是前提条件是同一个消费组中必须订阅相同主题,那应该怎么实现呢。
直接给代码:
java
package com.bluefoxyu.consumer;
import com.alibaba.fastjson.JSON;
import com.bluefoxyu.message.GeneralMessageEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* topic 对应主题
* consumerGroup 指定消费的分组
* RocketMQPushConsumerLifecycleListener 基础改监听器可以监听多个主题
*/
@Slf4j
@Component
@RocketMQMessageListener(
topic = "",
consumerGroup = "rocketmq-v2-demo-message-group_bluefoxyu" // 相同的 consumerGroup
)
public class GeneralMessageConsumer implements RocketMQListener<GeneralMessageEvent> , RocketMQPushConsumerLifecycleListener {
// 由于修改成可以订阅多个主题,在下面prepareStart就已经消费了,onMessage就不会执行了
@Override
public void onMessage(GeneralMessageEvent message) {
/*log.info("消费者GeneralMessageConsumer接收到消息,消息体:{}", JSON.toJSONString(message));
System.out.println("General message = " + message);*/
}
@Override
public void prepareStart(DefaultMQPushConsumer consumer) {
try {
consumer.subscribe("rocketmq-demo-message_topic_general", "*");
consumer.subscribe("rocketmq-demo-message_topic_TagA", "*");
consumer.subscribe("rocketmq-demo-message_topic_TagB", "*");
consumer.registerMessageListener((List<MessageExt> messages, ConsumeConcurrentlyContext context) -> {
if (CollectionUtils.isNotEmpty(messages)) {
for (MessageExt message : messages) {
log.info("消费者GeneralMessageConsumer接收到消息,消费完成:消费主题为:{} , 消费的消息为:{}" ,message.getTopic() ,new String(message.getBody()));
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
} catch (MQClientException e) {
log.error("消费失败,异常消息为:{}",e.getMessage());
}
}
}
java
package com.bluefoxyu.consumer;
import com.alibaba.fastjson.JSON;
import com.bluefoxyu.message.GeneralMessageEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* topic 对应主题
* consumerGroup 指定消费的分组
* RocketMQPushConsumerLifecycleListener 基础改监听器可以监听多个主题
*/
@Slf4j
@Component
@RocketMQMessageListener(
topic = "",
consumerGroup = "rocketmq-v2-demo-message-group_bluefoxyu" // 相同的 consumerGroup
)
public class TagAMessageConsumer implements RocketMQListener<GeneralMessageEvent> , RocketMQPushConsumerLifecycleListener {
// 由于修改成可以订阅多个主题,在下面prepareStart就已经消费了,onMessage就不会执行了
@Override
public void onMessage(GeneralMessageEvent message) {
/*log.info("消费者TagAMessageConsumer接收到消息,消息体:{}", JSON.toJSONString(message));
System.out.println("tagA message = " + message);*/
}
@Override
public void prepareStart(DefaultMQPushConsumer consumer) {
try {
consumer.subscribe("rocketmq-demo-message_topic_general", "*");
consumer.subscribe("rocketmq-demo-message_topic_TagA", "*");
consumer.subscribe("rocketmq-demo-message_topic_TagB", "*");
consumer.registerMessageListener((List<MessageExt> messages, ConsumeConcurrentlyContext context) -> {
if (CollectionUtils.isNotEmpty(messages)) {
for (MessageExt message : messages) {
log.info("消费者TagAMessageConsumer接收到消息,消费完成:消费主题为:{} , 消费的消息为:{}" ,message.getTopic() ,new String(message.getBody()));
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
} catch (MQClientException e) {
log.error("消费失败,异常消息为:{}",e.getMessage());
}
}
}
java
package com.bluefoxyu.consumer;
import com.alibaba.fastjson.JSON;
import com.bluefoxyu.message.GeneralMessageEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* topic 对应主题
* consumerGroup 指定消费的分组
* RocketMQPushConsumerLifecycleListener 基础改监听器可以监听多个主题
*/
@Slf4j
@Component
@RocketMQMessageListener(
topic = "",
consumerGroup = "rocketmq-v2-demo-message-group_bluefoxyu" // 相同的 consumerGroup
)
public class TagBMessageConsumer implements RocketMQListener<GeneralMessageEvent> , RocketMQPushConsumerLifecycleListener {
// 由于修改成可以订阅多个主题,在下面prepareStart就已经消费了,onMessage就不会执行了
@Override
public void onMessage(GeneralMessageEvent message) {
/*log.info("消费者TagBMessageConsumer接收到消息,消息体:{}", JSON.toJSONString(message));
System.out.println("tagB message = " + message);*/
}
@Override
public void prepareStart(DefaultMQPushConsumer consumer) {
try {
consumer.subscribe("rocketmq-demo-message_topic_general", "*");
consumer.subscribe("rocketmq-demo-message_topic_TagA", "*");
consumer.subscribe("rocketmq-demo-message_topic_TagB", "*");
consumer.registerMessageListener((List<MessageExt> messages, ConsumeConcurrentlyContext context) -> {
if (CollectionUtils.isNotEmpty(messages)) {
for (MessageExt message : messages) {
log.info("消费者TagBMessageConsumer接收到消息,消费完成:消费主题为:{} , 消费的消息为:{}" ,message.getTopic() ,new String(message.getBody()));
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
} catch (MQClientException e) {
log.error("消费失败,异常消息为:{}",e.getMessage());
}
}
}
分别测试三个接口:
参考这位大佬的博客:
rocketmq (消费者消费同一个消费组,不同的topic)_rocketmq一个消费组消费多个topic-CSDN博客
11、多个消费者组订阅相同主题
这个业务经常是有的,希望订阅同一种业务,但是有不同的实现,这时候就需要使用Tag过滤标签来区分了。
1、实现消费者
MessageConsumerA:
java
package com.bluefoxyu.consumer;
import com.alibaba.fastjson.JSON;
import com.bluefoxyu.message.GeneralMessageEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
import static com.bluefoxyu.constant.RocketMQConstant.*;
/**
* topic 对应主题
* consumerGroup 指定消费的分组
*/
@Slf4j
@Component
@RocketMQMessageListener(
topic = MESSAGE_TOPIC_2,
consumerGroup = MESSAGE_CONSUMER_GROUP_A,
selectorExpression = MESSAGE_TAG_A
)
public class MessageConsumerA implements RocketMQListener<GeneralMessageEvent>{
@Override
public void onMessage(GeneralMessageEvent message) {
log.info("[消费者MessageConsumerA] 接收到消息,消息体:{},消息:{}", JSON.toJSONString(message), JSON.toJSONString(message.getBody()));
}
}
MessageConsumerB:
java
package com.bluefoxyu.consumer;
import com.alibaba.fastjson.JSON;
import com.bluefoxyu.message.GeneralMessageEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
import static com.bluefoxyu.constant.RocketMQConstant.*;
/**
* topic 对应主题
* consumerGroup 指定消费的分组
*/
@Slf4j
@Component
@RocketMQMessageListener(
topic = MESSAGE_TOPIC_2,
consumerGroup = MESSAGE_CONSUMER_GROUP_B,
selectorExpression = MESSAGE_TAG_B
)
public class MessageConsumerB implements RocketMQListener<GeneralMessageEvent>{
@Override
public void onMessage(GeneralMessageEvent message) {
log.info("[消费者MessageConsumerB] 接收到消息,消息体:{},消息:{}", JSON.toJSONString(message), JSON.toJSONString(message.getBody()));
}
}
MessageConsumerC:
java
package com.bluefoxyu.consumer;
import com.alibaba.fastjson.JSON;
import com.bluefoxyu.message.GeneralMessageEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
import static com.bluefoxyu.constant.RocketMQConstant.*;
/**
* topic 对应主题
* consumerGroup 指定消费的分组
*/
@Slf4j
@Component
@RocketMQMessageListener(
topic = MESSAGE_TOPIC_2,
consumerGroup = MESSAGE_CONSUMER_GROUP_C,
selectorExpression = MESSAGE_TAG_C
)
public class MessageConsumerC implements RocketMQListener<GeneralMessageEvent>{
@Override
public void onMessage(GeneralMessageEvent message) {
log.info("[消费者MessageConsumerC] 接收到消息,消息体:{},消息:{}", JSON.toJSONString(message), JSON.toJSONString(message.getBody()));
}
}
2、编写接口发送消息
再controller添加那三个接口
java
@PostMapping("/send/topic2/messageA")
public String sendTopic2MessageA() {
String keys = UUID.randomUUID().toString();
GeneralMessageEvent generalMessageEvent = GeneralMessageEvent.builder()
.body("Message for A")
.keys(keys)
.build();
SendResult sendResult = generalMessageDemoProduce.sendMessage(
MESSAGE_TOPIC_2,
MESSAGE_TAG_A,
keys,
generalMessageEvent
);
// 返回发送成功的状态名
return sendResult.getSendStatus().name();
}
@PostMapping("/send/topic2/messageB")
public String sendTopic3MessageB() {
String keys = UUID.randomUUID().toString();
GeneralMessageEvent generalMessageEvent = GeneralMessageEvent.builder()
.body("Message for B")
.keys(keys)
.build();
SendResult sendResult = generalMessageDemoProduce.sendMessage(
MESSAGE_TOPIC_2,
MESSAGE_TAG_B,
keys,
generalMessageEvent
);
return sendResult.getSendStatus().name();
}
@PostMapping("/send/topic2/messageC")
public String sendTopic2MessageC() {
String keys = UUID.randomUUID().toString();
GeneralMessageEvent generalMessageEvent = GeneralMessageEvent.builder()
.body("Message for C")
.keys(keys)
.build();
SendResult sendResult = generalMessageDemoProduce.sendMessage(
MESSAGE_TOPIC_2,
MESSAGE_TAG_C,
keys,
generalMessageEvent
);
return sendResult.getSendStatus().name();
}
3、接口测试
消费成功!
也可以参考一个佬的博客:RocketMQ,同一个topic下是否可以通过不同的tag来进行订阅吗?_rocketmq一个topic多个tag-CSDN博客
当然,如果订阅不同主题也是没问题的,这里就不作演示了。
四、文末大佬好文
最后加上两个大佬好文,感觉讲的都很好:
面试官:RocketMQ同一个消费组内的消费者订阅了不同tag,会有问题吗?_rocketmq 订阅多个tag-CSDN博客
面试官:RocketMQ一个消费组内订阅同一个主题不同的TAG为什么会丢消息_为什么rocketmq相同消费组不同tag会有问题-CSDN博客
对于一个消费者组订阅同一个主题不同tag会丢消息,在前几天从0到1学习的时候,以为是可以的,但是踩了大坑。