开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 10 天,点击查看活动详情
RocketMQ 是 Apache 旗下一个优秀的开源项目,它起源于阿里,经受过“双十一”的性能考验,是一个性能十分优秀的消息队列中间件。 今天,我将介绍如何安装并将其整合到 Spring Boot 项目中。
01-在 Linux 安装 RocketMQ
首先,到官网 下载 二进制包,目前的最新版本是 5.0.0。 解压后的目录包括:
rocketmq-all-5.0.0-bin-release
├── benchmark
├── bin
| ├── mqnamesrv
| ├── runserver.sh
| ├── mqbroker
| ├── runbroker.sh
| ├── ...
│ └── tools.sh
├── conf
├── lib
├── LICENSE
├── NOTICE
└── README.md
需要运行的进程有两个 NameServer 和 Broker,其中:
- NameServer 保存的是 Topic 路由信息、管理 Broker 等,可以理解为一个“服务注册中心”。
- Broker 是消息队列的核心进程。
启动 NameServer 的命令如下:
nohup sh bin/mqnamesrv &
启动 Broker 的命令如下:
nohup sh bin/mqbroker -c conf/broker.conf &
注:启动之前请检查下宿主机上的 JDK 版本,如果是 jdk11 需要修改 runbroker.sh 和 runserver.sh 中 JVM 参数。
如果宿主机配置比较一般,建议把 runbroker.sh 中的堆栈配的小一点,例如 -server -Xms1g -Xmx1g -Xmn512m。
如果 NameServer 和 Broker 的日志,路径在 ~/rocketmqlogs/*.log,出现如下说明服务启动正常:
-- nameserver
The Name Server boot success. serializeType=JSON
-- broker
The broker[broker-a, ***:10911] boot success. serializeType=JSON and name server is localhost:9876
RocketMQ 官方还提供了一个图形化管理界面,项目名称为 RocketMQ-Dashboard,地址在 apache/rocketmq-dashboard。 下载后,可以通过如下编译并运行:
mvn clean package -Dmaven.test.skip=true
java -jar target/rocketmq-dashboard-1.0.1-SNAPSHOT.jar
注:编译之前,需要在 ./src/main/resources/application.yml 修改 NameServer 的地址。
启动后,DashBoard 默认监听 8080 端口,浏览器访问后可以看到如下界面:
02-实现简单的生产者、消费者模型
首先,引入依赖:
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
</dependency>
创建一个生产者:
public class SimpleProducerExample {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("producer");
producer.setNamesrvAddr("example.samson.self:9876"); // 到 NameServer 的连接
producer.setSendMsgTimeout(60000);
producer.start();
String[] tags = new String[]{"TagA", "TagB"};
// 向 DemoTopic 中发送 10 条消息,TagA 5条,TagB 5条
for (int i = 0; i < 10; ++i) {
Message message = new Message("DemoTopic", tags[i%2], ("test message-" + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.send(message);
System.out.println(sendResult);
}
producer.shutdown();
}
}
创建一个消费者:
public class SimpleConsumerExample {
public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer");
consumer.setNamesrvAddr("example.samson.self:9876");
consumer.subscribe("DemoTopic", "TagA"); // 订阅 DemoTopic,并且只接受带有 TagA 的消息
// 注册监听器,当收到消息时回调
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt messageExt : list) {
System.out.println("消费消息:" + new String(messageExt.getBody())); // 打印收到的消息
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; // 消费成功
}
});
consumer.start();
System.out.println("Consumer Started.");
}
}
先运行消费者线程,再启动生产者,从消费者的日志可以看到如下内容:
Consumer Started.
消费消息:test message-0
消费消息:test message-2
消费消息:test message-4
消费消息:test message-6
消费消息:test message-8
03-集成到 Spring Boot 应用中
集成到 Spring Boot 应用中,可以使用 starter 项目,Maven 依赖如下:
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
</dependency>
在 Spring Boot 应用中,NameServer 的地址可以在 application.yml 中配置:
rocketmq:
name-server: example.samson.self:9876
对于生产者,可以通过 RocketMQTemplate rocketMQTemplate; 来获得。
@SpringBootApplication
public class RocketMqExampleApplication implements CommandLineRunner {
@Autowired
private RocketMQTemplate rocketMQTemplate;
public static void main(String[] args) {
SpringApplication.run(RocketMqExampleApplication.class);
}
@Override
public void run(String... args) throws Exception {
String[] tags = new String[]{"TagB", "TagA"};
for (int i = 0; i < 10 ; i++) {
Message message = new Message("DemoTopic", tags[i%2], ("Spring test message-" + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult send = rocketMQTemplate.getProducer().send(message);
System.out.println(send);
}
}
}
我在 Spring Boot 应用中,创建了一个与上节中类似的生产者,它也会发送 10 条消息到 DemoTopic 中。
在 Spring Boot 应用中,创建消费者需要使用 @RocketMQMessageListener 注解。
@Component
@RocketMQMessageListener(consumerGroup = "spring-consumer", topic = "DemoTopic", selectorType = SelectorType.TAG, selectorExpression = "TagB")
public class SpringRmqListener implements RocketMQListener<String> {
@Override
public void onMessage(String s) {
System.out.println("处理消息:" + s);
}
}
我创建了一个消费者,它同样订阅了 DemoTopic,只不过它只接受带 TagB 的消息。 运行一下,可以看到日志中如下输出:
2023-02-12 18:35:15.041 INFO 10000 --- [ main] s.s.e.r.s.RocketMqExampleApplication : Started RocketMqExampleApplication in 2.378 seconds (JVM running for 3.199)
处理消息:test message-3
处理消息:test message-7
处理消息:test message-5
处理消息:test message-1
处理消息:test message-9
SendResult [sendStatus=SEND_OK, msgId=7F00000127102437C6DC3CA2FF8F0000, offsetMsgId=C0A87C1600002A9F0000000000000A2D, messageQueue=MessageQueue [topic=DemoTopic, brokerName=broker-a, queueId=2], queueOffset=3]
处理消息:Spring test message-0
SendResult [sendStatus=SEND_OK, msgId=7F00000127102437C6DC3CA2FF9C0001, offsetMsgId=C0A87C1600002A9F0000000000000B21, messageQueue=MessageQueue [topic=DemoTopic, brokerName=broker-a, queueId=3], queueOffset=3]
SendResult [sendStatus=SEND_OK, msgId=7F00000127102437C6DC3CA2FFA20002, offsetMsgId=C0A87C1600002A9F0000000000000C15, messageQueue=MessageQueue [topic=DemoTopic, brokerName=broker-a, queueId=0], queueOffset=2]
处理消息:Spring test message-2
SendResult [sendStatus=SEND_OK, msgId=7F00000127102437C6DC3CA2FFA80003, offsetMsgId=C0A87C1600002A9F0000000000000D09, messageQueue=MessageQueue [topic=DemoTopic, brokerName=broker-a, queueId=1], queueOffset=3]
SendResult [sendStatus=SEND_OK, msgId=7F00000127102437C6DC3CA2FFAD0004, offsetMsgId=C0A87C1600002A9F0000000000000DFD, messageQueue=MessageQueue [topic=DemoTopic, brokerName=broker-a, queueId=2], queueOffset=4]
处理消息:Spring test message-4
SendResult [sendStatus=SEND_OK, msgId=7F00000127102437C6DC3CA2FFB10005, offsetMsgId=C0A87C1600002A9F0000000000000EF1, messageQueue=MessageQueue [topic=DemoTopic, brokerName=broker-a, queueId=3], queueOffset=4]
SendResult [sendStatus=SEND_OK, msgId=7F00000127102437C6DC3CA2FFB70006, offsetMsgId=C0A87C1600002A9F0000000000000FE5, messageQueue=MessageQueue [topic=DemoTopic, brokerName=broker-a, queueId=0], queueOffset=3]
处理消息:Spring test message-6
SendResult [sendStatus=SEND_OK, msgId=7F00000127102437C6DC3CA2FFBB0007, offsetMsgId=C0A87C1600002A9F00000000000010D9, messageQueue=MessageQueue [topic=DemoTopic, brokerName=broker-a, queueId=1], queueOffset=4]
SendResult [sendStatus=SEND_OK, msgId=7F00000127102437C6DC3CA2FFBF0008, offsetMsgId=C0A87C1600002A9F00000000000011CD, messageQueue=MessageQueue [topic=DemoTopic, brokerName=broker-a, queueId=2], queueOffset=5]
处理消息:Spring test message-8
SendResult [sendStatus=SEND_OK, msgId=7F00000127102437C6DC3CA2FFC50009, offsetMsgId=C0A87C1600002A9F00000000000012C1, messageQueue=MessageQueue [topic=DemoTopic, brokerName=broker-a, queueId=3], queueOffset=5]
注:前面的 message-3/7/5/1/9 是之前测试滞留在 DemoTopic 中的消息(未被上节中的 SimpleConsumerExample 消费掉)。 后面的 message-0/2/4/6/8 是 Spring Boot 应用中的生产者产生的带 TagB 的消息。
04-总结
今天,我介绍了如何在 Linux 安装并启动 RocketMQ 消息队列中间件。 然后,由通过原生 rocketmq-client 接口实现了一个简单的生产者、消费者模型。 最后,在 Spring Boot 应用中集成了 RocketMQ,并使用 Spring Boot 中的 API 实现了类似的生产者、消费者模型,并进行了实验。 希望今天的内容能对你有所帮助。