前言
RocketMQ是由Alibaba用Java开发、现已加入到Apache下的一个分布式消息中间件,具有高性能、低延迟,高可靠性,下面是与Springboot的整合使用过程
1、RocketMQ 安装过程请自行百度,本篇不涉及
2、个人觉得 RocketMQ 比 RabbitMQ 优势明显,如:RocketMQ 支持事务、消息过滤、消息查询,当队列较多、消息堆积时性能也很稳定(毕竟挺过这么多的双11),而 RabbitMQ 性能明显下降,并且 RocketMQ 有可视化的中文管理后台,非常友好,不过活跃度上 RabbitMQ 要高出不少,相关资料 RocketMQ 要相对少一点
在 pom.xml 中加入以下依赖(这里用的2.0.4版本,19年底推出,完全够用,新的2.1版本有部分改动)
<dependency>
<groupId>org.apache.rocketmqgroupId>
<artifactId>rocketmq-spring-boot-starterartifactId>
<version>2.0.4version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.62version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
在 application.yml 中添加以下配置
rocketmq:
name-server: 192.168.1.224:9876 # 访问地址
producer:
group: Pro_Group # 必须指定group
send-message-timeout: 3000 # 消息发送超时时长,默认3s
retry-times-when-send-failed: 3 # 同步发送消息失败重试次数,默认2
retry-times-when-send-async-failed: 3 # 异步发送消息失败重试次数,默认2
新建 MQProducerService 类,如下(建议自己手动跟着敲一遍,熟悉rocketMQTemplate使用方式)
@Slf4j
@Component
public class MQProducerService {
@Value("${rocketmq.producer.send-message-timeout}")
private Integer messageTimeOut;
// 建议正常规模项目统一用一个TOPIC
private static final String topic = "RLT_TEST_TOPIC";
// 直接注入使用,用于发送消息到broker服务器
@Autowired
private RocketMQTemplate rocketMQTemplate;
/**
* 普通发送(这里的参数对象User可以随意定义,可以发送个对象,也可以是字符串等)
*/
public void send(User user) {
rocketMQTemplate.convertAndSend(topic + ":tag1", user);
// rocketMQTemplate.send(topic + ":tag1", MessageBuilder.withPayload(user).build()); // 等价于上面一行
}
/**
* 发送同步消息(阻塞当前线程,等待broker响应发送结果,这样不太容易丢失消息)
* (msgBody也可以是对象,sendResult为返回的发送结果)
*/
public SendResult sendMsg(String msgBody) {
SendResult sendResult = rocketMQTemplate.syncSend(topic, MessageBuilder.withPayload(msgBody).build());
log.info("【sendMsg】sendResult={}", JSON.toJSONString(sendResult));
return sendResult;
}
/**
* 发送异步消息(通过线程池执行发送到broker的消息任务,执行完后回调:在SendCallback中可处理相关成功失败时的逻辑)
* (适合对响应时间敏感的业务场景)
*/
public void sendAsyncMsg(String msgBody) {
rocketMQTemplate.asyncSend(topic, MessageBuilder.withPayload(msgBody).build(), new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
// 处理消息发送成功逻辑
}
@Override
public void onException(Throwable throwable) {
// 处理消息发送异常逻辑
}
});
}
/**
* 发送延时消息(上面的发送同步消息,delayLevel的值就为0,因为不延时)
* 在start版本中 延时消息一共分为18个等级分别为:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
*/
public void sendDelayMsg(String msgBody, int delayLevel) {
rocketMQTemplate.syncSend(topic, MessageBuilder.withPayload(msgBody).build(), messageTimeOut, delayLevel);
}
/**
* 发送单向消息(只负责发送消息,不等待应答,不关心发送结果,如日志)
*/
public void sendOneWayMsg(String msgBody) {
rocketMQTemplate.sendOneWay(topic, MessageBuilder.withPayload(msgBody).build());
}
/**
* 发送带tag的消息,直接在topic后面加上":tag"
*/
public SendResult sendTagMsg(String msgBody) {
return rocketMQTemplate.syncSend(topic + ":tag2", MessageBuilder.withPayload(msgBody).build());
}
}
上面写的这几个消息发送方法,你应该注意到了: 第一个方法和最后一个方法的参数 topic 和其它的不一样
其实这是 rocketmq 和 springboot 整合后设置 Tag 的方式(Tag:用于区分过滤同一主题下的不同业务类型的消息,非常实用)
在项目里往mq写入消息时,最好每条消息都带上tag,用于消费时根据业务过滤
在 rocketmq-spring-boot-starter 中,Tag 的设置方式: 在 topic后面加上 “:tagName”
源码中是以 “:”进行分割的,前面的是 topic,后面的就是 tag,截图如下:
另外,从上面的截图中可以看到“key”的设置方式,发送消息时在header中设置:
MessageBuilder.withPayload(msgBody).setHeader(RocketMQHeaders.KEYS, "key1")
新建 MQConsumerService 类(本案例中我就以上面生产者中第一个和最后一个加了tag的消息进行消费)
@Slf4j
@Component
public class MQConsumerService {
// topic需要和生产者的topic一致,consumerGroup属性是必须指定的,内容可以随意
// selectorExpression的意思指的就是tag,默认为“*”,不设置的话会监听所有消息
@Service
@RocketMQMessageListener(topic = "RLT_TEST_TOPIC", selectorExpression = "tag1", consumerGroup = "Con_Group_One")
public class ConsumerSend implements RocketMQListener<User> {
// 监听到消息就会执行此方法
@Override
public void onMessage(User user) {
log.info("监听到消息:user={}", JSON.toJSONString(user));
}
}
// 注意:这个ConsumerSend2和上面ConsumerSend在没有添加tag做区分时,不能共存,
// 不然生产者发送一条消息,这两个都会去消费,如果类型不同会有一个报错,所以实际运用中最好加上tag,写这只是让你看知道就行
@Service
@RocketMQMessageListener(topic = "RLT_TEST_TOPIC", consumerGroup = "Con_Group_Two")
public class ConsumerSend2 implements RocketMQListener<String> {
@Override
public void onMessage(String str) {
log.info("监听到消息:str={}", str);
}
}
// MessageExt:是一个消息接收通配符,不管发送的是String还是对象,都可接收,当然也可以像上面明确指定类型(我建议还是指定类型较方便)
@Service
@RocketMQMessageListener(topic = "RLT_TEST_TOPIC", selectorExpression = "tag2", consumerGroup = "Con_Group_Three")
public class Consumer implements RocketMQListener<MessageExt> {
@Override
public void onMessage(MessageExt messageExt) {
byte[] body = messageExt.getBody();
String msg = new String(body);
log.info("监听到消息:msg={}", msg);
}
}
}
消费者这里要注意设置的参数要正确,我这里为了方便就写在一个类里
这里消费者会直接监听生产者发送的消息,一旦生产者那边发送消息,对应这里就会消费
另外可以对注解 @RocketMQMessageListener 点进去看看它的属性参数,都是非常熟悉的
这里就直接建个Controller吧
@RestController
@RequestMapping("/rocketmq")
public class RocketMQController {
@Autowired
private MQProducerService mqProducerService;
@GetMapping("/send")
public void send() {
User user = User.getUser();
mqProducerService.send(user);
}
@GetMapping("/sendTag")
public Result<SendResult> sendTag() {
SendResult sendResult = mqProducerService.sendTagMsg("带有tag的字符消息");
return Result.success(sendResult);
}
}
用postman调用测试:
(1)http://localhost:8080/rocketmq/send
(2)http://localhost:8080/rocketmq/sendTag
你也可以试试消费消息时不指定tag,它会监听消费所有消息
开始提到,rocketmq有一个可视化的后台,你也可以在里面找到你发的消息和消费情况
总结: