Spring Boot「25」整合 RocketMQ 消息队列中间件

232 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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 端口,浏览器访问后可以看到如下界面:

img_rocketmq_dashboard.png

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 实现了类似的生产者、消费者模型,并进行了实验。 希望今天的内容能对你有所帮助。