springboot结合rocketmq的使用以及遇到的问题
rocketmq是一款低延迟、高并发、高可用、高可靠的分布式消息中间件。消息队列 RocketMQ 可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性。
首先需要下载安装rocketmq:
1.官网 https://rocketmq.apache.org/zh/docs/quickStart/01quickstart
2.点击下载二进制包
下载完后进行解压,根据文件夹的路径进行下面的环境变量配置
3.配置环境变量
ROCKETMQ_HOME:E:\rocketmq-all-5.2.0\rocketmq-all-5.2.0
4.启动mqnamesrv
在bin目录下双击mqnamesrv.cmd
窗口不闪退,这样就是启动成功,不要关闭窗口
5.启动mqbroker(有顺序,先需要启动mqnamesrv)
同样在bin的目录里,我们输入cmd
输入 start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true
注意:在这里我遇到了一个问题
错误: 找不到或无法加载主类 Files\Java\jdk1.8.0_311\lib\dt.jar;C:\Program
根据度娘指引,需要修改runbroker.cmd文件种 %CLASSPATH%为其加上双引号
保存修改,重新运行start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true
会弹出一个新的窗口,运行成功 不要关闭窗口
到此,rocketmq服务就已经全部启动了
接下来开始把rocketmq引入springboot
文件目录(我们测试程序只需要这四个文件):
1.配置pom
<!-- rocketmq-->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
<!-- 还有其它需要的jar包自由引入(注:fastjson不要使用低于1.2.60版本,会有安全漏洞) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency><dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
2.application.properties配置
#rocketmq
rocketmq.name-server=127.0.0.1:9876
rocketmq.producer.group=Pro_Group
rocketmq.producer.send-message-timeout=3000
rocketmq.producer.retry-times-when-send-async-failed=3
rocketmq.producer.retry-times-when-send-failed=3
3.编写实体类
package com.gykg.yizhichun.entity;
import lombok.Data;
@Data
public class User {
private String id;
private String name;
private Integer age;
private String sex;
private String desc;
}
4.编写生产者类
package com.gykg.yizhichun.producer;
import com.alibaba.fastjson.JSON;
import com.gykg.yizhichun.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
@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;
/**
* Tag:用于区分过滤同一主题下的不同业务类型的消息,非常实用
* 普通发送(这里的参数对象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) {
// 处理消息发送成功逻辑
log.info("【sendMsg】sendResult={}", JSON.toJSONString(sendResult));
}
@Override
public void onException(Throwable throwable) {
// 处理消息发送异常逻辑
log.info("【sendMsg】sendResult={}", "发送异常" + throwable.getMessage());
}
});
}
/**
* 发送延时消息(上面的发送同步消息,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());
}
/***
* 服务生产者,顺序消息
* 把消息确保投递到同一条queue
* 保证了消息的顺序性
*/
public void sendFIFOMsg(List<User> users) {
//顺序消息
//选择器规则构建
rocketMQTemplate.setMessageQueueSelector((list, message, o) -> {
int id = Integer.valueOf((String) o);
int hash = (id % list.size());
return list.get(hash);
});
if (!CollectionUtils.isEmpty(users)) {
for (User user : users) {
MessageBuilder.withPayload(users.toString()).build();
rocketMQTemplate.sendOneWayOrderly(topic+":sendFIFOMsg", user, String.valueOf(user.getId()));
}
}
}
}
5.编写消费者类
package com.gykg.yizhichun.consumer;
import com.alibaba.fastjson.JSON;
import com.gykg.yizhichun.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Slf4j
@Component
public class MQConsumerService {
// Tag:用于区分过滤同一主题下的不同业务类型的消息,非常实用
// 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("tag1监听到消息:user={}", JSON.toJSONString(user));
}
}
// 注意:这个ConsumerSend2和上面ConsumerSend在没有添加tag做区分时,不能共存,
// 不然生产者发送一条消息,这两个都会去消费,如果类型不同会有一个报错,所以实际运用中最好加上tag,写这只是让你看知道就行
@Service
@RocketMQMessageListener(topic = "RLT_TEST_TOPIC", consumerGroup = "Con_Group_Two",selectorExpression = "xxx")
public class ConsumerSend2 implements RocketMQListener<String> {
@Override
public void onMessage(String str) {
log.info("ConsumerSend2监听到消息: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("tag2监听到消息:msg={}", msg);
}
}
/**
* 消费者顺序消费消息
* 顺序消费
*/
@Service
@RocketMQMessageListener(consumerGroup = "Orderly-Consumer", topic = "RLT_TEST_TOPIC",selectorExpression = "sendFIFOMsg", consumeMode = ConsumeMode.ORDERLY)
public class OrderlyConsumer implements RocketMQListener<MessageExt> {
@Override
public void onMessage(MessageExt message) {
System.out.println("线程"+Thread.currentThread()+"内容为:"
+ new String(message.getBody())+
"队列序号:"+message.getQueueId()+",消息msgId:"+message.getMsgId());
}
}
}
6.编写控制器类
package com.gykg.yizhichun.controller;
import com.gykg.yizhichun.entity.User;
import com.gykg.yizhichun.producer.MQProducerService;
import org.apache.rocketmq.client.producer.SendResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/rocketmq")
public class MqController {
@Autowired
private MQProducerService mqProducerService;
@GetMapping("/send")
public void send() {
User user = new User();
user.setAge(28);
user.setName("曹震");
user.setSex("男");
mqProducerService.send(user);
}
@GetMapping("/sendTag")
public ResponseEntity<SendResult> sendTag() {
SendResult sendResult = mqProducerService.sendTagMsg("带有tag的字符消息");
return ResponseEntity.ok(sendResult);
}
@GetMapping("/sendMsg")
public ResponseEntity<SendResult> sendMsg() {
SendResult sendResult = mqProducerService.sendMsg("曹震测试");
return ResponseEntity.ok(sendResult);
}
@GetMapping("/sendFIFOMsg")
public void sendFIFOMsg() {
List<User> users = new ArrayList<>();
User user = new User();
user.setId("1");
user.setSex("男");
user.setName("曹震");
user.setAge(28);
user.setDesc("创建订单");
users.add(user);
User user1 = new User();
user1.setId("2");
user1.setSex("男");
user1.setName("贾耀旗");
user1.setAge(25);
user1.setDesc("创建订单");
users.add(user1);
mqProducerService.sendFIFOMsg(users);
}
}
然后就是测试接口拉,这里要注意的是:不要忘记把producer和consumer这两个包加入扫描,不然是会获取不到bean 的
我们运行起来,测试 /rocketmq/sendFIFOMsg 接口,输出这个,测试成功
巨人的肩膀:https://blog.csdn.net/qq_23126581/article/details/132496439
https://blog.csdn.net/Messy_Cat/article/details/124108281
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了