RocketMQ快速入门与Spring Boot整合实践

引言

RocketMQ是阿里巴巴开源的一款分布式消息中间件,具有高吞吐量、高可用性、低延迟的特点,广泛应用于电商、金融、物联网等领域的消息处理场景。相较于Kafka和RabbitMQ,RocketMQ在金融级场景下的可靠性表现尤为突出,支持事务消息、顺序消息、延时消息等高级特性。

本文将从零开始,详细介绍RocketMQ的核心概念、环境搭建、与Spring Boot项目的完整整合过程,帮助开发者快速掌握这一强大的消息中间件。

RocketMQ核心概念解析

1. 消息模型组件

Producer(生产者)

  • 负责发送消息到RocketMQ服务器
  • 支持同步发送、异步发送、单向发送三种方式
  • 可以按消息类型分为普通生产者、事务生产者

Consumer(消费者)

  • 从RocketMQ服务器拉取并消费消息
  • 支持推模式(Push)和拉模式(Pull)两种消费模型
  • 分为集群消费和广播消费两种模式

Topic(主题)

  • 消息的逻辑分类单元
  • 生产者发送消息到指定Topic
  • 消费者订阅Topic来消费消息
  • Topic与Queue是多对一关系

Message Queue(消息队列)

  • Topic的物理存储单元
  • 一个Topic可以包含多个Queue
  • 实现消息的分片存储和负载均衡

NameServer(名称服务器)

  • 提供轻量级的路由注册发现服务
  • 管理Broker的路由信息
  • 无状态设计,集群部署简单

Broker(代理服务器)

  • 消息存储和转发的核心组件
  • 负责消息的接收、存储、投递
  • 支持主从复制和故障切换

2. 消息类型

普通消息

  • 无特殊功能要求的常规消息
  • 支持三种发送方式:同步、异步、单向

顺序消息

  • 保证消息的消费顺序与发送顺序一致
  • 分为全局顺序和分区顺序
  • 通过Message Queue实现顺序保证

事务消息

  • 保证消息发送与本地事务的最终一致性
  • 采用两阶段提交协议
  • 适用于分布式事务场景

延时消息

  • 消息发送后延迟一定时间才可被消费
  • 支持特定的延时级别(如1s、5s、10s等)
  • 适用于订单超时取消等场景

Spring Boot整合实现

1. 项目依赖配置

Maven配置

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>

    <!-- Spring Boot父项目 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.15</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>rocketmq-springboot-demo</artifactId>
    <version>1.0.0</version>
    <name>rocketmq-springboot-demo</name>
    <description>RocketMQ Spring Boot整合示例</description>

    <properties>
        <java.version>1.8</java.version>
        <rocketmq-spring-boot-starter.version>2.3.3</rocketmq-spring-boot-starter.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Web依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- RocketMQ Spring Boot Starter -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>${rocketmq-spring-boot-starter.version}</version>
        </dependency>

        <!-- Lombok(简化代码) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- JSON处理 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.43</version>
        </dependency>

        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Gradle配置

gradle 复制代码
plugins {
    id 'org.springframework.boot' version '2.7.15'
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
    id 'java'
}

group = 'com.example'
version = '1.0.0'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.apache.rocketmq:rocketmq-spring-boot-starter:2.3.3'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    implementation 'com.alibaba:fastjson:2.0.43'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

2. 配置文件

application.yml配置

yaml 复制代码
server:
  port: 8080

spring:
  application:
    name: rocketmq-springboot-demo

# RocketMQ配置
rocketmq:
  # NameServer地址,多个地址用分号分隔
  name-server: 127.0.0.1:9876
  
  # 生产者配置
  producer:
    # 生产者组名
    group: demo-producer-group
    # 发送消息超时时间(毫秒)
    send-message-timeout: 3000
    # 消息体超过该值则启用压缩(字节)
    compress-message-body-threshold: 4096
    # 最大消息大小(字节,默认4M)
    max-message-size: 4194304
    # 同步发送失败重试次数
    retry-times-when-send-failed: 2
    # 异步发送失败重试次数
    retry-times-when-send-async-failed: 0
    # 发送失败时是否尝试重试到其他Broker
    retry-next-server: true
  
  # 消费者配置
  consumer:
    # 消费者组名
    group: demo-consumer-group
    # 最小消费线程数
    consume-thread-min: 5
    # 最大消费线程数
    consume-thread-max: 20
    # 消息消费失败重试次数(-1表示无限重试,16次后进入死信队列)
    max-reconsume-times: 3
    # 拉取消息的间隔时间(毫秒)
    pull-batch-size: 32

application.properties配置

properties 复制代码
server.port=8080
spring.application.name=rocketmq-springboot-demo

# RocketMQ NameServer地址
rocketmq.name-server=127.0.0.1:9876

# 生产者配置
rocketmq.producer.group=demo-producer-group
rocketmq.producer.send-message-timeout=3000
rocketmq.producer.retry-times-when-send-failed=2

# 消费者配置
rocketmq.consumer.group=demo-consumer-group
rocketmq.consumer.consume-thread-min=5
rocketmq.consumer.consume-thread-max=20
rocketmq.consumer.max-reconsume-times=3

3. 版本兼容性说明

RocketMQ版本 Spring Boot版本 rocketmq-spring-boot-starter版本
5.3.x 2.7.x, 3.x 2.3.3+
5.2.x 2.6.x, 2.7.x 2.2.4 - 2.3.2
5.1.x 2.5.x, 2.6.x 2.2.0 - 2.2.3
4.9.x 2.3.x - 2.7.x 2.1.x - 2.2.x

注意:

  • Spring Boot 3.x需要使用JDK 17+
  • 推荐使用稳定的版本组合进行生产部署
  • 升级前务必在测试环境验证兼容性

生产者实现

1. 同步发送

同步发送是最可靠的发送方式,发送后会等待Broker的响应,适用于对可靠性要求高的场景。

java 复制代码
package com.example.rocketmq.producer;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.UUID;

/**
 * 同步消息发送服务
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Service
public class SyncMessageProducer {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 发送简单字符串消息(同步方式)
     * 
     * @param topic 主题名称
     * @param message 消息内容
     * @return 发送结果
     */
    public SendResult sendSimpleMessage(String topic, String message) {
        log.info("开始发送同步消息,topic: {}, message: {}", topic, message);
        
        try {
            // 使用convertAndSend方法直接发送
            SendResult sendResult = rocketMQTemplate.syncSend(topic, message);
            
            log.info("同步消息发送成功,topic: {}, messageId: {}, sendStatus: {}", 
                    topic, sendResult.getMsgId(), sendResult.getSendStatus());
            
            return sendResult;
        } catch (Exception e) {
            log.error("同步消息发送失败,topic: {}, message: {}", topic, message, e);
            throw new RuntimeException("消息发送失败", e);
        }
    }

    /**
     * 发送带Tag的消息(同步方式)
     * Topic和Tag使用冒号分隔:topic:tag
     * 
     * @param topic 主题名称
     * @param tag 消息标签
     * @param message 消息内容
     * @return 发送结果
     */
    public SendResult sendTagMessage(String topic, String tag, String message) {
        log.info("开始发送带Tag的同步消息,topic: {}, tag: {}, message: {}", topic, tag, message);
        
        try {
            // 使用topic:tag格式发送
            String destination = topic + ":" + tag;
            SendResult sendResult = rocketMQTemplate.syncSend(destination, message);
            
            log.info("带Tag消息发送成功,topic: {}, tag: {}, messageId: {}", 
                    topic, tag, sendResult.getMsgId());
            
            return sendResult;
        } catch (Exception e) {
            log.error("带Tag消息发送失败,topic: {}, tag: {}", topic, tag, e);
            throw new RuntimeException("消息发送失败", e);
        }
    }

    /**
     * 发送对象消息(同步方式)
     * 消息会自动序列化为JSON格式
     * 
     * @param topic 主题名称
     * @param object 消息对象
     * @return 发送结果
     */
    public SendResult sendObjectMessage(String topic, Object object) {
        log.info("开始发送对象消息,topic: {}, object: {}", topic, object);
        
        try {
            SendResult sendResult = rocketMQTemplate.syncSend(topic, object);
            
            log.info("对象消息发送成功,topic: {}, messageId: {}", 
                    topic, sendResult.getMsgId());
            
            return sendResult;
        } catch (Exception e) {
            log.error("对象消息发送失败,topic: {}, object: {}", topic, object, e);
            throw new RuntimeException("消息发送失败", e);
        }
    }

    /**
     * 发送带Message的消息(同步方式)
     * 可以设置消息头、消息属性等
     * 
     * @param topic 主题名称
     * @param message 消息内容
     * @return 发送结果
     */
    public SendResult sendMessageWithHeader(String topic, String message) {
        log.info("开始发送带Header的同步消息,topic: {}, message: {}", topic, message);
        
        try {
            // 构建Spring Message对象
            Message<String> springMessage = MessageBuilder
                    .withPayload(message)
                    .setHeader("header-key", "header-value")
                    .setHeader("message-id", UUID.randomUUID().toString())
                    .build();
            
            SendResult sendResult = rocketMQTemplate.syncSend(topic, springMessage);
            
            log.info("带Header消息发送成功,topic: {}, messageId: {}", 
                    topic, sendResult.getMsgId());
            
            return sendResult;
        } catch (Exception e) {
            log.error("带Header消息发送失败,topic: {}, message: {}", topic, message, e);
            throw new RuntimeException("消息发送失败", e);
        }
    }

    /**
     * 发送带超时时间的同步消息
     * 
     * @param topic 主题名称
     * @param message 消息内容
     * @param timeout 超时时间(毫秒)
     * @return 发送结果
     */
    public SendResult sendWithTimeout(String topic, String message, long timeout) {
        log.info("开始发送带超时时间的同步消息,topic: {}, timeout: {}ms, message: {}", 
                topic, timeout, message);
        
        try {
            SendResult sendResult = rocketMQTemplate.syncSend(topic, message, timeout);
            
            log.info("带超时时间消息发送成功,topic: {}, messageId: {}", 
                    topic, sendResult.getMsgId());
            
            return sendResult;
        } catch (Exception e) {
            log.error("带超时时间消息发送失败,topic: {}, timeout: {}ms", topic, timeout, e);
            throw new RuntimeException("消息发送失败", e);
        }
    }
}

2. 异步发送

异步发送不会阻塞当前线程,通过回调函数处理发送结果,适用于高吞吐量场景。

java 复制代码
package com.example.rocketmq.producer;

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.stereotype.Service;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 异步消息发送服务
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Service
public class AsyncMessageProducer {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 发送异步消息
     * 
     * @param topic 主题名称
     * @param message 消息内容
     */
    public void sendAsyncMessage(String topic, String message) {
        log.info("开始发送异步消息,topic: {}, message: {}", topic, message);
        
        rocketMQTemplate.asyncSend(topic, message, new SendCallback() {
            /**
             * 发送成功的回调
             */
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("异步消息发送成功,topic: {}, messageId: {}, sendStatus: {}", 
                        topic, sendResult.getMsgId(), sendResult.getSendStatus());
                
                // 这里可以执行发送成功后的业务逻辑
                // 例如:记录日志、更新数据库等
            }

            /**
             * 发送失败的回调
             */
            @Override
            public void onException(Throwable e) {
                log.error("异步消息发送失败,topic: {}, message: {}", topic, message, e);
                
                // 这里可以执行失败后的补偿逻辑
                // 例如:重试、记录到数据库、发送告警等
            }
        });
    }

    /**
     * 发送带超时时间的异步消息
     * 
     * @param topic 主题名称
     * @param message 消息内容
     * @param timeout 超时时间(毫秒)
     */
    public void sendAsyncWithTimeout(String topic, String message, long timeout) {
        log.info("开始发送带超时时间的异步消息,topic: {}, timeout: {}ms, message: {}", 
                topic, timeout, message);
        
        rocketMQTemplate.asyncSend(topic, message, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("带超时时间异步消息发送成功,topic: {}, messageId: {}", 
                        topic, sendResult.getMsgId());
            }

            @Override
            public void onException(Throwable e) {
                log.error("带超时时间异步消息发送失败,topic: {}, timeout: {}ms", 
                        topic, timeout, e);
            }
        }, timeout);
    }

    /**
     * 发送异步消息并等待结果(用于测试场景)
     * 
     * @param topic 主题名称
     * @param message 消息内容
     * @throws InterruptedException 线程中断异常
     */
    public void sendAsyncAndWait(String topic, String message) throws InterruptedException {
        log.info("开始发送异步消息并等待结果,topic: {}, message: {}", topic, message);
        
        CountDownLatch latch = new CountDownLatch(1);
        
        rocketMQTemplate.asyncSend(topic, message, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("异步消息发送成功,topic: {}, messageId: {}", 
                        topic, sendResult.getMsgId());
                latch.countDown();
            }

            @Override
            public void onException(Throwable e) {
                log.error("异步消息发送失败,topic: {}, message: {}", topic, message, e);
                latch.countDown();
            }
        });
        
        // 等待回调执行完成,最多等待5秒
        latch.await(5, TimeUnit.SECONDS);
        log.info("异步发送等待完成");
    }
}

3. 单向发送

单向发送不等待Broker的响应,可靠性最低但性能最好,适用于对可靠性要求不高的日志收集等场景。

java 复制代码
package com.example.rocketmq.producer;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 单向消息发送服务
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Service
public class OnewayMessageProducer {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 发送单向消息
     * 单向发送不等待Broker响应,可靠性最低但性能最好
     * 
     * @param topic 主题名称
     * @param message 消息内容
     */
    public void sendOnewayMessage(String topic, String message) {
        log.info("开始发送单向消息,topic: {}, message: {}", topic, message);
        
        try {
            rocketMQTemplate.sendOneWay(topic, message);
            log.info("单向消息发送完成(不等待响应),topic: {}", topic);
            
            // 注意:单向发送无法获取发送结果,需要自行处理失败情况
        } catch (Exception e) {
            log.error("单向消息发送异常,topic: {}, message: {}", topic, message, e);
            // 单向发送通常不重试,记录日志即可
        }
    }

    /**
     * 发送带Tag的单向消息
     * 
     * @param topic 主题名称
     * @param tag 消息标签
     * @param message 消息内容
     */
    public void sendOnewayWithTag(String topic, String tag, String message) {
        log.info("开始发送带Tag的单向消息,topic: {}, tag: {}, message: {}", topic, tag, message);
        
        try {
            String destination = topic + ":" + tag;
            rocketMQTemplate.sendOneWay(destination, message);
            log.info("带Tag的单向消息发送完成,topic: {}, tag: {}", topic, tag);
        } catch (Exception e) {
            log.error("带Tag的单向消息发送异常,topic: {}, tag: {}", topic, tag, e);
        }
    }

    /**
     * 批量发送单向消息
     * 
     * @param topic 主题名称
     * @param messages 消息列表
     */
    public void sendBatchOneway(String topic, String... messages) {
        log.info("开始批量发送单向消息,topic: {}, count: {}", topic, messages.length);
        
        for (String message : messages) {
            try {
                rocketMQTemplate.sendOneWay(topic, message);
                log.debug("单向消息发送:{}", message);
            } catch (Exception e) {
                log.error("单向消息发送失败:{}", message, e);
            }
        }
        
        log.info("批量单向消息发送完成,topic: {}, count: {}", topic, messages.length);
    }
}

4. 生产者Controller(测试接口)

java 复制代码
package com.example.rocketmq.controller;

import com.example.rocketmq.producer.AsyncMessageProducer;
import com.example.rocketmq.producer.OnewayMessageProducer;
import com.example.rocketmq.producer.SyncMessageProducer;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * 消息生产者控制器
 * 提供REST接口用于测试消息发送
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@RestController
@RequestMapping("/api/producer")
public class ProducerController {

    @Autowired
    private SyncMessageProducer syncMessageProducer;
    
    @Autowired
    private AsyncMessageProducer asyncMessageProducer;
    
    @Autowired
    private OnewayMessageProducer onewayMessageProducer;

    /**
     * 发送同步消息
     * 
     * @param topic 主题名称
     * @param message 消息内容
     * @return 发送结果
     */
    @PostMapping("/sync/send")
    public Map<String, Object> sendSyncMessage(
            @RequestParam String topic,
            @RequestParam String message) {
        
        log.info("接收同步发送请求,topic: {}, message: {}", topic, message);
        
        Map<String, Object> result = new HashMap<>();
        
        try {
            SendResult sendResult = syncMessageProducer.sendSimpleMessage(topic, message);
            
            result.put("success", true);
            result.put("messageId", sendResult.getMsgId());
            result.put("sendStatus", sendResult.getSendStatus());
            result.put("queueId", sendResult.getMessageQueue().getQueueId());
            result.put("offset", sendResult.getQueueOffset());
            
        } catch (Exception e) {
            log.error("同步消息发送失败", e);
            result.put("success", false);
            result.put("error", e.getMessage());
        }
        
        return result;
    }

    /**
     * 发送带Tag的同步消息
     * 
     * @param topic 主题名称
     * @param tag 消息标签
     * @param message 消息内容
     * @return 发送结果
     */
    @PostMapping("/sync/sendWithTag")
    public Map<String, Object> sendSyncWithTags(
            @RequestParam String topic,
            @RequestParam String tag,
            @RequestParam String message) {
        
        log.info("接收带Tag的同步发送请求,topic: {}, tag: {}, message: {}", 
                topic, tag, message);
        
        Map<String, Object> result = new HashMap<>();
        
        try {
            SendResult sendResult = syncMessageProducer.sendTagMessage(topic, tag, message);
            
            result.put("success", true);
            result.put("messageId", sendResult.getMsgId());
            result.put("sendStatus", sendResult.getSendStatus());
            
        } catch (Exception e) {
            log.error("带Tag的同步消息发送失败", e);
            result.put("success", false);
            result.put("error", e.getMessage());
        }
        
        return result;
    }

    /**
     * 发送异步消息
     * 
     * @param topic 主题名称
     * @param message 消息内容
     * @return 发送请求状态
     */
    @PostMapping("/async/send")
    public Map<String, Object> sendAsyncMessage(
            @RequestParam String topic,
            @RequestParam String message) {
        
        log.info("接收异步发送请求,topic: {}, message: {}", topic, message);
        
        Map<String, Object> result = new HashMap<>();
        
        try {
            asyncMessageProducer.sendAsyncMessage(topic, message);
            
            result.put("success", true);
            result.put("message", "异步消息已发送,请查看日志获取发送结果");
            
        } catch (Exception e) {
            log.error("异步消息发送失败", e);
            result.put("success", false);
            result.put("error", e.getMessage());
        }
        
        return result;
    }

    /**
     * 发送单向消息
     * 
     * @param topic 主题名称
     * @param message 消息内容
     * @return 发送请求状态
     */
    @PostMapping("/oneway/send")
    public Map<String, Object> sendOnewayMessage(
            @RequestParam String topic,
            @RequestParam String message) {
        
        log.info("接收单向发送请求,topic: {}, message: {}", topic, message);
        
        Map<String, Object> result = new HashMap<>();
        
        try {
            onewayMessageProducer.sendOnewayMessage(topic, message);
            
            result.put("success", true);
            result.put("message", "单向消息已发送,不等待响应");
            
        } catch (Exception e) {
            log.error("单向消息发送失败", e);
            result.put("success", false);
            result.put("error", e.getMessage());
        }
        
        return result;
    }
}

消费者实现

1. 推模式消费者(Push Consumer)

推模式是RocketMQ的默认消费模式,RocketMQ会主动将消息推送给消费者。

java 复制代码
package com.example.rocketmq.consumer;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

/**
 * 推模式消费者(Push Consumer)
 * 
 * RocketMQ会主动将消息推送给消费者
 * 适用于实时性要求高的场景
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Service
@RocketMQMessageListener(
    topic = "test-topic",                    // 订阅的主题
    consumerGroup = "test-consumer-group",   // 消费者组名
    selectorExpression = "*",                // Tag过滤表达式,*表示订阅所有Tag
    consumeMode = org.apache.rocketmq.spring.annotation.ConsumeMode.CONCURRENTLY, // 并发消费
    messageModel = org.apache.rocketmq.spring.annotation.MessageModel.CLUSTERING  // 集群消费模式
)
public class PushStringConsumer implements RocketMQListener<String> {

    /**
     * 消息消费方法
     * 
     * @param message 消息内容
     */
    @Override
    public void onMessage(String message) {
        log.info("接收到消息: {}", message);
        
        try {
            // TODO: 处理业务逻辑
            
            // 模拟业务处理
            processMessage(message);
            
            log.info("消息处理成功: {}", message);
            
        } catch (Exception e) {
            log.error("消息处理失败: {}", message, e);
            // 抛出异常会触发消息重试
            throw new RuntimeException("消息处理失败", e);
        }
    }

    /**
     * 处理消息的业务逻辑
     * 
     * @param message 消息内容
     */
    private void processMessage(String message) {
        // 这里编写具体的业务处理逻辑
        // 例如:数据库操作、调用其他服务等
        
        log.debug("处理消息业务逻辑: {}", message);
    }
}

2. 对象消息消费者

java 复制代码
package com.example.rocketmq.consumer;

import com.example.rocketmq.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

/**
 * 对象消息消费者
 * 
 * 接收并处理对象类型的消息
 * 消息会自动反序列化为指定的Java对象
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Service
@RocketMQMessageListener(
    topic = "order-topic",
    consumerGroup = "order-consumer-group",
    selectorExpression = "*"
)
public class OrderConsumer implements RocketMQListener<Order> {

    /**
     * 接收Order对象消息
     * 
     * @param order 订单对象
     */
    @Override
    public void onMessage(Order order) {
        log.info("接收到订单消息: orderId={}, userId={}, amount={}", 
                order.getOrderId(), order.getUserId(), order.getAmount());
        
        try {
            // TODO: 处理订单相关的业务逻辑
            processOrder(order);
            
            log.info("订单处理成功: orderId={}", order.getOrderId());
            
        } catch (Exception e) {
            log.error("订单处理失败: orderId={}", order.getOrderId(), e);
            throw new RuntimeException("订单处理失败", e);
        }
    }

    /**
     * 处理订单业务逻辑
     * 
     * @param order 订单对象
     */
    private void processOrder(Order order) {
        // 例如:扣减库存、更新订单状态等
        log.info("执行订单处理逻辑: orderId={}", order.getOrderId());
    }
}

3. 带Tag过滤的消费者

java 复制代码
package com.example.rocketmq.consumer;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

/**
 * 带Tag过滤的消费者
 * 
 * 使用selectorExpression属性进行消息过滤
 * 支持简单的Tag表达式
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Service
@RocketMQMessageListener(
    topic = "order-topic",
    consumerGroup = "order-tag-consumer-group",
    // 只消费tagA或tagB的消息
    selectorExpression = "tagA || tagB"
)
public class TagFilterConsumer implements RocketMQListener<String> {

    @Override
    public void onMessage(String message) {
        log.info("接收到tagA或tagB的消息: {}", message);
        
        try {
            // 处理消息
            processMessage(message);
            
        } catch (Exception e) {
            log.error("消息处理失败: {}", message, e);
            throw new RuntimeException("消息处理失败", e);
        }
    }

    private void processMessage(String message) {
        log.debug("处理消息: {}", message);
    }
}

4. 顺序消息消费者

java 复制代码
package com.example.rocketmq.consumer;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

/**
 * 顺序消息消费者
 * 
 * 通过设置consumeMode为ORDERLY来实现顺序消费
 * 保证同一队列中的消息按顺序消费
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Service
@RocketMQMessageListener(
    topic = "order-topic",
    consumerGroup = "order-sequential-consumer-group",
    selectorExpression = "*",
    // 关键:设置为顺序消费模式
    consumeMode = ConsumeMode.ORDERLY,
    messageModel = MessageModel.CLUSTERING
)
public class SequentialConsumer implements RocketMQListener<String> {

    @Override
    public void onMessage(String message) {
        log.info("接收到顺序消息: {}", message);
        
        try {
            // 顺序处理消息
            // 注意:因为是顺序消费,处理时间不宜过长,否则会影响后续消息的处理
            processSequentialMessage(message);
            
            log.info("顺序消息处理成功: {}", message);
            
        } catch (Exception e) {
            log.error("顺序消息处理失败: {}", message, e);
            // 顺序消费失败时,该消息会被挂起,不会自动重试
            // 需要人工介入或等待Broker重新投递
            throw new RuntimeException("顺序消息处理失败", e);
        }
    }

    /**
     * 顺序处理消息
     * 
     * @param message 消息内容
     */
    private void processSequentialMessage(String message) {
        // 按顺序处理业务逻辑
        // 例如:同一订单的多个操作必须按顺序执行
        log.debug("顺序处理消息: {}", message);
    }
}

5. 广播模式消费者

java 复制代码
package com.example.rocketmq.consumer;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;

/**
 * 广播模式消费者
 * 
 * 使用MessageModel.BROADCASTING模式
 * 每个消费者都会收到所有消息
 * 适用于需要所有消费者都处理相同消息的场景
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Service
@RocketMQMessageListener(
    topic = "broadcast-topic",
    consumerGroup = "broadcast-consumer-group",
    selectorExpression = "*",
    // 关键:设置为广播模式
    messageModel = MessageModel.BROADCASTING
)
public class BroadcastConsumer implements RocketMQListener<String> {

    @Override
    public void onMessage(String message) {
        log.info("接收到广播消息: {}", message);
        
        try {
            // 处理广播消息
            processBroadcastMessage(message);
            
        } catch (Exception e) {
            log.error("广播消息处理失败: {}", message, e);
            throw new RuntimeException("广播消息处理失败", e);
        }
    }

    private void processBroadcastMessage(String message) {
        // 广播消息处理逻辑
        log.debug("处理广播消息: {}", message);
    }
}

6. 拉模式消费者(Pull Consumer)

拉模式消费者需要主动从Broker拉取消息,适用于需要精确控制消费进度的场景。

java 复制代码
package com.example.rocketmq.consumer;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.MQPullConsumerScheduleService;
import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.client.consumer.PullTaskCallback;
import org.apache.rocketmq.client.consumer.PullTaskContext;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 拉模式消费者(Pull Consumer)
 * 
 * 主动从Broker拉取消息,精确控制消费进度
 * 适用于需要自定义消费逻辑的场景
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Component
public class PullConsumer {

    @Value("${rocketmq.name-server}")
    private String nameServer;

    @Value("${rocketmq.consumer.group}")
    private String consumerGroup;

    private MQPullConsumerScheduleService scheduleService;

    /**
     * 初始化拉模式消费者
     */
    @PostConstruct
    public void init() throws Exception {
        log.info("初始化拉模式消费者");

        scheduleService = new MQPullConsumerScheduleService(consumerGroup);
        scheduleService.getDefaultMQPullConsumer().setNamesrvAddr(nameServer);
        scheduleService.setMessageModel(org.apache.rocketmq.common.protocol.heartbeat.MessageModel.CLUSTERING);

        // 注册拉取任务
        scheduleService.registerPullTaskCallback("pull-topic", new PullTaskCallback() {
            @Override
            public void doPullTask(MessageQueue mq, PullTaskContext context) {
                DefaultMQPullConsumer pullConsumer = context.getPullConsumer();
                
                try {
                    // 获取该队列的拉取offset
                    long offset = pullConsumer.fetchConsumeOffset(mq, false);
                    if (offset < 0) {
                        offset = 0;
                    }

                    // 拉取消息(每次最多32条)
                    PullResult pullResult = pullConsumer.pull(mq, "*", offset, 32);

                    log.info("拉取消息结果: mq={}, offset={}, pullStatus={}", 
                            mq, offset, pullResult.getPullStatus());

                    switch (pullResult.getPullStatus()) {
                        case FOUND:
                            // 处理拉取到的消息
                            List<MessageExt> messages = pullResult.getMsgFoundList();
                            if (messages != null && !messages.isEmpty()) {
                                for (MessageExt message : messages) {
                                    try {
                                        // 处理消息
                                        processMessage(message);
                                        
                                        // 更新消费进度
                                        pullConsumer.updateConsumeOffset(mq, pullResult.getNextBeginOffset());
                                    } catch (Exception e) {
                                        log.error("消息处理失败: messageId={}", message.getMsgId(), e);
                                        // 根据业务需求决定是否更新offset
                                    }
                                }
                            }
                            break;
                            
                        case NO_MATCHED_MSG:
                            log.debug("没有匹配的消息: mq={}, offset={}", mq, offset);
                            break;
                            
                        case NO_NEW_MSG:
                            log.debug("没有新消息: mq={}, offset={}", mq, offset);
                            break;
                            
                        case OFFSET_ILLEGAL:
                            log.warn("offset非法: mq={}, offset={}", mq, offset);
                            // 重置offset
                            pullConsumer.updateConsumeOffset(mq, pullResult.getNextBeginOffset());
                            break;
                            
                        default:
                            log.warn("未知的拉取状态: {}", pullResult.getPullStatus());
                            break;
                    }

                } catch (Exception e) {
                    log.error("拉取消息异常: mq={}", mq, e);
                }
            }
        });

        // 启动消费者
        scheduleService.start();
        log.info("拉模式消费者启动成功");
    }

    /**
     * 处理消息
     * 
     * @param message 消息对象
     */
    private void processMessage(MessageExt message) {
        String msgBody = new String(message.getBody());
        log.info("处理消息: messageId={}, body={}", message.getMsgId(), msgBody);
        
        // TODO: 处理业务逻辑
    }

    /**
     * 销毁消费者
     */
    @PreDestroy
    public void destroy() {
        log.info("关闭拉模式消费者");
        if (scheduleService != null) {
            scheduleService.shutdown();
        }
    }
}

消息类型应用

1. 普通消息

普通消息是最基础的消息类型,已在生产者实现部分详细介绍,这里补充完整示例。

java 复制代码
package com.example.rocketmq.producer;

import com.example.rocketmq.entity.Order;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * 普通消息生产者
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Service
public class NormalMessageProducer {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 发送普通字符串消息
     */
    public void sendStringMessage(String topic, String message) {
        rocketMQTemplate.convertAndSend(topic, message);
        log.info("发送普通字符串消息: topic={}, message={}", topic, message);
    }

    /**
     * 发送对象消息(会自动序列化为JSON)
     */
    public void sendObjectMessage(String topic, Order order) {
        rocketMQTemplate.convertAndSend(topic, order);
        log.info("发送对象消息: topic={}, orderId={}", topic, order.getOrderId());
    }

    /**
     * 发送带属性的Message对象
     */
    public void sendMessageWithProperties(String topic, String message) {
        Map<String, Object> headers = new HashMap<>();
        headers.put("messageType", "NORMAL");
        headers.put("timestamp", System.currentTimeMillis());
        headers.put("traceId", "trace-" + System.currentTimeMillis());
        
        Message<String> msg = MessageBuilder.withPayload(message)
                .copyHeaders(headers)
                .build();
        
        rocketMQTemplate.send(topic, msg);
        log.info("发送带属性的Message对象: topic={}, message={}", topic, message);
    }

    /**
     * 发送Map类型消息
     */
    public void sendMapMessage(String topic, Map<String, Object> data) {
        rocketMQTemplate.convertAndSend(topic, data);
        log.info("发送Map类型消息: topic={}, keys={}", topic, data.keySet());
    }
}

2. 顺序消息

顺序消息保证消息的消费顺序与发送顺序一致,适用于需要严格顺序的场景。

java 复制代码
package com.example.rocketmq.producer;

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.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * 顺序消息生产者
 * 
 * 通过设置HashKey将同一业务的消息发送到同一个队列
 * 从而保证顺序消费
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Service
public class OrderedMessageProducer {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 发送顺序消息(同步方式)
     * 
     * @param topic 主题名称
     * @param hashKey 用于选择队列的键,同一hashKey的消息会发送到同一队列
     * @param message 消息内容
     * @return 发送结果
     */
    public SendResult sendOrderedMessage(String topic, String hashKey, String message) {
        log.info("发送顺序消息: topic={}, hashKey={}, message={}", topic, hashKey, message);
        
        Message<String> msg = MessageBuilder.withPayload(message).build();
        
        // 使用syncSendOrdered方法发送顺序消息
        SendResult sendResult = rocketMQTemplate.syncSendOrdered(topic, msg, hashKey);
        
        log.info("顺序消息发送成功: topic={}, messageId={}, queueId={}", 
                topic, sendResult.getMsgId(), sendResult.getMessageQueue().getQueueId());
        
        return sendResult;
    }

    /**
     * 发送顺序消息(异步方式)
     * 
     * @param topic 主题名称
     * @param hashKey 用于选择队列的键
     * @param message 消息内容
     */
    public void sendOrderedMessageAsync(String topic, String hashKey, String message) {
        log.info("异步发送顺序消息: topic={}, hashKey={}, message={}", topic, hashKey, message);
        
        Message<String> msg = MessageBuilder.withPayload(message).build();
        
        rocketMQTemplate.asyncSendOrdered(topic, msg, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("异步顺序消息发送成功: topic={}, messageId={}", 
                        topic, sendResult.getMsgId());
            }

            @Override
            public void onException(Throwable e) {
                log.error("异步顺序消息发送失败: topic={}, hashKey={}", topic, hashKey, e);
            }
        }, hashKey);
    }

    /**
     * 批量发送同一业务ID的顺序消息
     * 
     * @param topic 主题名称
     * @param businessId 业务ID(作为hashKey)
     * @param messages 消息列表
     */
    public void sendOrderedMessagesBatch(String topic, String businessId, List<String> messages) {
        log.info("批量发送顺序消息: topic={}, businessId={}, count={}", 
                topic, businessId, messages.size());
        
        for (int i = 0; i < messages.size(); i++) {
            String message = messages.get(i);
            
            try {
                // 同步发送,保证顺序
                sendOrderedMessage(topic, businessId, message);
                
                // 适当延迟,避免发送过快
                if (i < messages.size() - 1) {
                    Thread.sleep(10);
                }
                
            } catch (InterruptedException e) {
                log.error("批量发送顺序消息被中断", e);
                Thread.currentThread().interrupt();
                break;
            }
        }
        
        log.info("批量顺序消息发送完成: topic={}, businessId={}", topic, businessId);
    }

    /**
     * 订单场景示例:发送订单状态变更消息
     * 同一订单的状态变更消息必须按顺序处理
     */
    public void sendOrderStatusMessage(String orderId, String oldStatus, String newStatus) {
        String topic = "order-status-topic";
        // 使用orderId作为hashKey,保证同一订单的消息有序
        String hashKey = orderId;
        
        String message = String.format("订单ID: %s, 状态变更: %s -> %s", 
                orderId, oldStatus, newStatus);
        
        sendOrderedMessage(topic, hashKey, message);
    }

    /**
     * 订单场景示例:发送完整的订单生命周期消息
     */
    public void sendOrderLifecycleMessages(String orderId) {
        String topic = "order-lifecycle-topic";
        
        // 定义订单生命周期的各个阶段
        List<String> lifecycleMessages = new ArrayList<>();
        lifecycleMessages.add(orderId + ": 创建订单");
        lifecycleMessages.add(orderId + ": 支付订单");
        lifecycleMessages.add(orderId + ": 发货");
        lifecycleMessages.add(orderId + ": 收货");
        lifecycleMessages.add(orderId + ": 评价");
        
        // 批量发送,使用orderId作为hashKey保证顺序
        sendOrderedMessagesBatch(topic, orderId, lifecycleMessages);
    }
}

3. 事务消息

事务消息是RocketMQ的核心特性之一,用于保证分布式场景下消息发送与本地事务的最终一致性。

java 复制代码
package com.example.rocketmq.transaction;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;

/**
 * 事务消息监听器
 * 
 * 负责执行本地事务和回查事务状态
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Component
public class OrderTransactionListener implements RocketMQLocalTransactionListener {

    /**
     * 存储事务状态
     * key: transactionId
     * value: 事务状态 (UNKNOWN=0, COMMIT=1, ROLLBACK=2)
     * 生产环境应该使用数据库或Redis持久化存储
     */
    private ConcurrentHashMap<String, Integer> localTransactionMap = new ConcurrentHashMap<>();

    /**
     * 执行本地事务
     * 
     * 当半消息发送成功后,RocketMQ会回调此方法执行本地事务
     * 
     * @param msg 消息对象
     * @param arg 业务参数
     * @return 事务状态
     */
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        // 获取事务ID
        String transactionId = (String) msg.getHeaders().get(RocketMQHeaders.TRANSACTION_ID);
        String orderId = (String) msg.getHeaders().get("orderId");
        
        log.info("执行本地事务: transactionId={}, orderId={}", transactionId, orderId);
        
        try {
            // TODO: 执行本地事务(例如:创建订单)
            boolean success = executeOrderTransaction(orderId);
            
            if (success) {
                // 本地事务成功,提交消息
                localTransactionMap.put(transactionId, 1);
                log.info("本地事务执行成功,提交消息: transactionId={}", transactionId);
                return RocketMQLocalTransactionState.COMMIT;
            } else {
                // 本地事务失败,回滚消息
                localTransactionMap.put(transactionId, 2);
                log.warn("本地事务执行失败,回滚消息: transactionId={}", transactionId);
                return RocketMQLocalTransactionState.ROLLBACK;
            }
            
        } catch (Exception e) {
            log.error("本地事务执行异常,返回UNKNOWN状态: transactionId={}", transactionId, e);
            
            // 事务状态未知,等待回查
            localTransactionMap.put(transactionId, 0);
            return RocketMQLocalTransactionState.UNKNOWN;
        }
    }

    /**
     * 检查本地事务状态
     * 
     * 当executeLocalTransaction返回UNKNOWN或超时未响应时,
     * RocketMQ会回调此方法检查本地事务状态
     * 
     * @param msg 消息对象
     * @return 事务状态
     */
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
        String transactionId = (String) msg.getHeaders().get(RocketMQHeaders.TRANSACTION_ID);
        String orderId = (String) msg.getHeaders().get("orderId");
        
        log.info("检查本地事务状态: transactionId={}, orderId={}", transactionId, orderId);
        
        Integer status = localTransactionMap.get(transactionId);
        
        if (status == null) {
            // 未知状态,继续返回UNKNOWN,等待下次回查
            log.warn("事务状态未知,返回UNKNOWN: transactionId={}", transactionId);
            return RocketMQLocalTransactionState.UNKNOWN;
        }
        
        switch (status) {
            case 1:
                // 已提交
                log.info("事务已提交: transactionId={}", transactionId);
                return RocketMQLocalTransactionState.COMMIT;
                
            case 2:
                // 已回滚
                log.info("事务已回滚: transactionId={}", transactionId);
                return RocketMQLocalTransactionState.ROLLBACK;
                
            case 0:
            default:
                // 未知或进行中,继续返回UNKNOWN
                log.info("事务进行中或状态未知,返回UNKNOWN: transactionId={}", transactionId);
                return RocketMQLocalTransactionState.UNKNOWN;
        }
    }

    /**
     * 执行订单事务(模拟)
     * 
     * @param orderId 订单ID
     * @return 执行结果
     */
    private boolean executeOrderTransaction(String orderId) {
        try {
            // TODO: 这里执行实际的业务逻辑
            // 例如:
            // 1. 开启数据库事务
            // 2. 插入订单记录
            // 3. 扣减库存
            // 4. 提交事务
            
            log.info("执行订单事务逻辑: orderId={}", orderId);
            
            // 模拟事务执行
            // 实际业务中应该根据数据库操作结果返回
            return true;
            
        } catch (Exception e) {
            log.error("执行订单事务异常: orderId={}", orderId, e);
            return false;
        }
    }
}
java 复制代码
package com.example.rocketmq.producer;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

/**
 * 事务消息生产者
 * 
 * 用于发送事务消息,保证消息发送与本地事务的最终一致性
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Service
public class TransactionMessageProducer {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 发送事务消息
     * 
     * @param topic 主题名称
     * @param message 消息内容
     * @param orderId 业务ID(用于事务回查)
     * @return 事务发送结果
     */
    public TransactionSendResult sendTransactionMessage(String topic, String message, String orderId) {
        log.info("发送事务消息: topic={}, message={}, orderId={}", topic, message, orderId);
        
        // 构建消息,添加业务属性
        Message<String> msg = MessageBuilder
                .withPayload(message)
                .setHeader("orderId", orderId)
                .setHeader("timestamp", System.currentTimeMillis())
                .build();
        
        // 事务名称,必须与@RocketMQTransactionListener的transName属性一致
        String transactionName = "order-transaction";
        
        try {
            // 发送事务消息
            TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(
                    transactionName,  // 事务名称
                    topic,           // 主题
                    msg,             // 消息
                    null             // 业务参数
            );
            
            log.info("事务消息发送完成: topic={}, messageId={}, transactionId={}, localTransactionState={}", 
                    topic, result.getMsgId(), result.getTransactionId(), result.getLocalTransactionState());
            
            return result;
            
        } catch (Exception e) {
            log.error("事务消息发送失败: topic={}, orderId={}", topic, orderId, e);
            throw new RuntimeException("事务消息发送失败", e);
        }
    }

    /**
     * 发送订单创建事务消息
     * 
     * @param orderId 订单ID
     * @param userId 用户ID
     * @param amount 金额
     */
    public void sendOrderCreatedMessage(String orderId, String userId, String amount) {
        String topic = "order-transaction-topic";
        
        String message = String.format("订单创建: orderId=%s, userId=%s, amount=%s", 
                orderId, userId, amount);
        
        sendTransactionMessage(topic, message, orderId);
    }
}

4. 延时消息

延时消息允许消息在指定时间后才可被消费,适用于订单超时取消等场景。

java 复制代码
package com.example.rocketmq.producer;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

/**
 * 延时消息生产者
 * 
 * RocketMQ支持特定的延时级别:
 * 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
 * 
 * @author example
 * @date 2024-01-01
 */
@Slf4j
@Service
public class DelayMessageProducer {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 发送延时消息
     * 
     * @param topic 主题名称
     * @param message 消息内容
     * @param delayLevel 延时级别(1-18)
     */
    public void sendDelayMessage(String topic, String message, int delayLevel) {
        log.info("发送延时消息: topic={}, message={}, delayLevel={}", topic, message, delayLevel);
        
        Message<String> msg = MessageBuilder.withPayload(message).build();
        
        // 发送延时消息
        rocketMQTemplate.syncSend(topic, msg, 3000, delayLevel);
        
        log.info("延时消息发送成功: topic={}, delayLevel={}", topic, delayLevel);
    }

    /**
     * 发送订单超时取消消息
     * 延时级别16 = 30分钟
     * 
     * @param orderId 订单ID
     */
    public void sendOrderTimeoutMessage(String orderId) {
        String topic = "order-timeout-topic";
        String message = "订单超时未支付,需要取消: orderId=" + orderId;
        
        // 延时级别16表示30分钟后可消费
        sendDelayMessage(topic, message, 16);
        
        log.info("订单超时检查消息已发送: orderId={}, 延时时间=30分钟", orderId);
    }

    /**
     * 延时级别对照表
     * 
     * level 1:  1秒
     * level 2:  5秒
     * level 3:  10秒
     * level 4:  30秒
     * level 5:  1分钟
     * level 6:  2分钟
     * level 7:  3分钟
     * level 8:  4分钟
     * level 9:  5分钟
     * level 10: 6分钟
     * level 11: 7分钟
     * level 12: 8分钟
     * level 13: 9分钟
     * level 14: 10分钟
     * level 15: 20分钟
     * level 16: 30分钟
     * level 17: 1小时
     * level 18: 2小时
     */
    public int getDelayLevel(String delayDescription) {
        switch (delayDescription) {
            case "1s": return 1;
            case "5s": return 2;
            case "10s": return 3;
            case "30s": return 4;
            case "1m": return 5;
            case "2m": return 6;
            case "3m": return 7;
            case "4m": return 8;
            case "5m": return 9;
            case "6m": return 10;
            case "7m": return 11;
            case "8m": return 12;
            case "9m": return 13;
            case "10m": return 14;
            case "20m": return 15;
            case "30m": return 16;
            case "1h": return 17;
            case "2h": return 18;
            default: return 1;
        }
    }
}

完整代码示例

1. 实体类

java 复制代码
package com.example.rocketmq.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

/**
 * 订单实体类
 * 
 * @author example
 * @date 2024-01-01
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 订单ID
     */
    private String orderId;

    /**
     * 用户ID
     */
    private String userId;

    /**
     * 订单金额
     */
    private BigDecimal amount;

    /**
     * 订单状态
     */
    private String status;

    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 更新时间
     */
    private Date updateTime;
}

2. 主启动类

java 复制代码
package com.example.rocketmq;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * RocketMQ Spring Boot 整合示例启动类
 * 
 * @author example
 * @date 2024-01-01
 */
@SpringBootApplication
public class RocketmqSpringbootDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(RocketmqSpringbootDemoApplication.class, args);
        System.out.println("RocketMQ Spring Boot整合示例启动成功!");
    }
}

3. 单元测试

java 复制代码
package com.example.rocketmq;

import com.example.rocketmq.producer.AsyncMessageProducer;
import com.example.rocketmq.producer.OnewayMessageProducer;
import com.example.rocketmq.producer.SyncMessageProducer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 生产者单元测试
 * 
 * @author example
 * @date 2024-01-01
 */
@SpringBootTest
public class ProducerTest {

    @Autowired
    private SyncMessageProducer syncMessageProducer;

    @Autowired
    private AsyncMessageProducer asyncMessageProducer;

    @Autowired
    private OnewayMessageProducer onewayMessageProducer;

    /**
     * 测试同步消息发送
     */
    @Test
    public void testSyncSend() {
        System.out.println("=== 测试同步消息发送 ===");
        
        syncMessageProducer.sendSimpleMessage("test-topic", "这是一条同步消息");
        syncMessageProducer.sendTagMessage("test-topic", "tagA", "这是一条带tagA的同步消息");
        syncMessageProducer.sendTagMessage("test-topic", "tagB", "这是一条带tagB的同步消息");
        
        System.out.println("同步消息发送测试完成");
    }

    /**
     * 测试异步消息发送
     */
    @Test
    public void testAsyncSend() throws InterruptedException {
        System.out.println("=== 测试异步消息发送 ===");
        
        asyncMessageProducer.sendAsyncMessage("test-topic", "这是一条异步消息");
        
        // 等待异步回调执行
        Thread.sleep(2000);
        
        System.out.println("异步消息发送测试完成");
    }

    /**
     * 测试单向消息发送
     */
    @Test
    public void testOnewaySend() {
        System.out.println("=== 测试单向消息发送 ===");
        
        onewayMessageProducer.sendOnewayMessage("test-topic", "这是一条单向消息");
        
        System.out.println("单向消息发送测试完成");
    }

    /**
     * 测试批量发送
     */
    @Test
    public void testBatchSend() {
        System.out.println("=== 测试批量发送 ===");
        
        for (int i = 1; i <= 10; i++) {
            String message = "批量消息 #" + i;
            syncMessageProducer.sendSimpleMessage("test-topic", message);
        }
        
        System.out.println("批量发送测试完成");
    }

    /**
     * 压力测试
     */
    @Test
    public void testPressure() throws InterruptedException {
        System.out.println("=== 压力测试开始 ===");
        
        int messageCount = 1000;
        CountDownLatch latch = new CountDownLatch(messageCount);
        
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < messageCount; i++) {
            final int index = i;
            new Thread(() -> {
                try {
                    syncMessageProducer.sendSimpleMessage("test-topic", "压测消息 #" + index);
                } finally {
                    latch.countDown();
                }
            }).start();
        }
        
        latch.await(10, TimeUnit.SECONDS);
        
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        
        System.out.println("压力测试完成:");
        System.out.println("发送消息数: " + messageCount);
        System.out.println("总耗时: " + duration + "ms");
        System.out.println("平均TPS: " + (messageCount * 1000.0 / duration));
    }
}

4. Postman测试示例

1. 发送同步消息

复制代码
POST http://localhost:8080/api/producer/sync/send
Content-Type: application/x-www-form-urlencoded

topic=test-topic&message=Hello RocketMQ

响应示例:

json 复制代码
{
  "success": true,
  "messageId": "7F0000017B484724B2BC2E1E9B400000",
  "sendStatus": "SEND_OK",
  "queueId": 0,
  "offset": 0
}

2. 发送带Tag的同步消息

复制代码
POST http://localhost:8080/api/producer/sync/sendWithTag
Content-Type: application/x-www-form-urlencoded

topic=test-topic&tag=tagA&message=带Tag的消息

3. 发送异步消息

复制代码
POST http://localhost:8080/api/producer/async/send
Content-Type: application/x-www-form-urlencoded

topic=test-topic&message=异步消息

4. 发送单向消息

复制代码
POST http://localhost:8080/api/producer/oneway/send
Content-Type: application/x-www-form-urlencoded

topic=test-topic&message=单向消息

常见问题解决

1. 连接问题

问题:无法连接到NameServer

复制代码
connect to <127.0.0.1:9876> failed

解决方案:

  1. 检查NameServer是否启动
  2. 检查防火墙设置
  3. 检查配置文件中的name-server地址是否正确
  4. 确认端口9876未被占用
bash 复制代码
# 检查端口占用
netstat -ano | findstr 9876

# 检查NameServer进程
jps | findstr NamesrvStartup

2. 内存问题

问题:启动Broker时内存不足

复制代码
Error: occurred during initialization of VM
Could not reserve enough space for object heap

解决方案:

修改runbroker.cmd(Windows)或runbroker.sh(Linux)中的JVM参数

bash 复制代码
# Windows - 编辑 runbroker.cmd
# 将 -Xms2g -Xmx2g 改为 -Xms512m -Xmx512m

# Linux - 编辑 runbroker.sh
# 将 JAVA_OPT="${JAVA_OPT} -Xms2g -Xmx2g" 
# 改为 JAVA_OPT="${JAVA_OPT} -Xms512m -Xmx512m"

3. 消息消费失败

问题:消费者一直重试,消息堆积

复制代码
Consume Message Failed, message will retry

解决方案:

  1. 检查消费者代码逻辑
  2. 查看消费者日志定位错误原因
  3. 考虑实现幂等性处理
  4. 对于无法处理的消息,记录并手动处理,避免无限重试
java 复制代码
@Override
public void onMessage(String message) {
    try {
        // 业务处理逻辑
        processMessage(message);
    } catch (BusinessException e) {
        log.error("业务异常,消息处理失败: {}", message, e);
        // 业务异常,不再重试,直接消费掉
        // 可以记录到数据库进行人工处理
    } catch (Exception e) {
        log.error("系统异常,触发重试: {}", message, e);
        // 系统异常,抛出异常触发重试
        throw new RuntimeException(e);
    }
}

4. 顺序消息乱序

问题:顺序消息出现乱序

解决方案:

  1. 确保生产者使用syncSendOrdered方法发送
  2. 确保使用相同的hashKey
  3. 确保消费者设置consumeMode = ConsumeMode.ORDERLY
  4. 消费者处理逻辑不能抛出异常,否则消息会被挂起

5. 事务消息状态未知

问题:事务消息一直处于UNKNOWN状态,不断回查

解决方案:

  1. 检查executeLocalTransaction方法的返回值
  2. 检查checkLocalTransaction方法是否正确实现
  3. 确保事务状态被正确存储(生产环境使用数据库或Redis)
  4. 设置合理的事务超时时间
java 复制代码
// 在配置文件中设置事务超时时间
rocketmq.producer.transaction-timeout=60000  // 60秒

6. 消息重复消费

问题:消费者收到重复消息

解决方案:

实现消息幂等性处理

java 复制代码
@Override
public void onMessage(String message) {
    // 使用Redis实现幂等性
    String messageId = getMessageId(message); // 从消息中提取唯一ID
    String redisKey = "msg:processed:" + messageId;
    
    // 尝试设置Redis key
    boolean isNew = redisTemplate.opsForValue()
            .setIfAbsent(redisKey, "1", 24, TimeUnit.HOURS);
    
    if (!isNew) {
        log.info("消息已处理过,跳过: messageId={}", messageId);
        return;
    }
    
    // 处理消息
    processMessage(message);
}

7. 消费者启动失败

问题:消费者启动时抛出异常

复制代码
The consumer group has been created before

解决方案:

消费者组名必须唯一,确保不同的应用使用不同的消费者组名。

8. 消息发送超时

问题:消息发送超时

解决方案:

  1. 增加发送超时时间配置
  2. 检查网络连接
  3. 检查Broker负载情况
  4. 考虑使用异步发送提高吞吐量
yaml 复制代码
rocketmq:
  producer:
    send-message-timeout: 10000  # 增加到10秒

总结与扩展

1. 性能优化建议

生产者优化

  1. 批量发送:尽量使用批量发送减少网络请求
  2. 异步发送:高吞吐场景使用异步发送
  3. 合理设置超时:根据业务需求设置合理的超时时间
  4. 消息压缩:大消息开启压缩功能
  5. 连接池优化:调整Netty连接参数
java 复制代码
// 批量发送示例
List<Message> messages = new ArrayList<>();
for (int i = 0; i < 100; i++) {
    messages.add(MessageBuilder.withPayload("消息" + i).build());
}
rocketMQTemplate.syncSend(topic, messages);

消费者优化

  1. 并发消费:适当增加消费线程数
  2. 批量消费:开启批量消费减少拉取次数
  3. 消费限流:设置合理的消费限流参数
  4. 消息过滤:使用Tag减少不必要的消息传输
  5. 异步处理:消费端异步处理提高吞吐
yaml 复制代码
rocketmq:
  consumer:
    consume-thread-min: 20
    consume-thread-max: 50
    pull-batch-size: 64

Broker优化

  1. 内存配置:合理设置JVM堆内存
  2. 刷盘策略:根据可靠性要求选择刷盘策略
  3. 副本同步:配置合适的主从复制策略
  4. 清理策略:定期清理过期消息
  5. 网络优化:优化网络参数
properties 复制代码
# Broker配置优化
flushDiskType=ASYNC_FLUSH  # 异步刷盘(高性能)
brokerRole=ASYNC_MASTER    # 异步主从复制(高性能)
deleteWhen=04              # 每天凌晨4点执行清理
fileReservedTime=48        # 保留48小时

2. 生产环境注意事项

集群部署

  1. 多NameServer部署:至少2-3个NameServer节点
  2. Broker集群:至少2个Broker节点,配置主从复制
  3. 负载均衡:配置负载均衡策略
  4. 监控告警:部署监控系统,设置告警阈值
  5. 容灾方案:制定完善的容灾恢复方案

安全配置

  1. ACL控制:开启ACL权限控制
  2. 网络安全:配置防火墙规则
  3. 数据加密:敏感数据加密传输
  4. 访问审计:记录访问日志
  5. 密码安全:使用强密码并定期更换
properties 复制代码
# 开启ACL
aclEnable=true

# 白名单配置
whiteListEnable=true

监控指标

  1. 消息TPS:监控发送和消费TPS
  2. 消息堆积:监控消息堆积量
  3. 响应时间:监控消息处理延迟
  4. 错误率:监控发送和消费错误率
  5. 资源使用:监控CPU、内存、磁盘使用率

推荐监控工具:

  • RocketMQ Dashboard
  • Prometheus + Grafana
  • ELK日志分析

运维规范

  1. 版本管理:统一版本管理,做好兼容性测试
  2. 配置管理:使用配置中心统一管理配置
  3. 变更流程:严格执行变更审批流程
  4. 备份恢复:定期备份重要数据
  5. 文档维护:维护完整的运维文档

3. RocketMQ高级特性

1. 消息轨迹

开启消息轨迹功能,可以追踪消息的完整生命周期。

java 复制代码
// 生产者端开启消息轨迹
rocketmq.producer.enable-msg-trace=true
rocketmq.producer.customized-trace-topic=RMQ_SYS_TRACE_TOPIC

// 消费者端开启消息轨迹
rocketmq.consumer.enable-msg-trace=true
rocketmq.consumer.customized-trace-topic=RMQ_SYS_TRACE_TOPIC

2. 消息过滤

RocketMQ支持Tag过滤和SQL92语法过滤。

java 复制代码
// Tag过滤
@RocketMQMessageListener(
    topic = "test-topic",
    selectorExpression = "tagA || tagB"  // 订阅tagA或tagB
)

// SQL92过滤
@RocketMQMessageListener(
    topic = "test-topic",
    selectorType = SelectorType.SQL92,
    selectorExpression = "amount > 1000 AND status = 'PAID'"
)

3. 死信队列

消费失败的消息会进入死信队列,需要专门处理。

java 复制代码
// 死信队列Topic命名规则:%DLQ%消费者组名
// 示例:%DLQ%test-consumer-group

// 处理死信队列消息
@Service
@RocketMQMessageListener(
    topic = "%DLQ%test-consumer-group",
    consumerGroup = "dlq-handler-group"
)
public class DeadLetterQueueHandler implements RocketMQListener<MessageExt> {
    
    @Override
    public void onMessage(MessageExt message) {
        log.error("收到死信消息: msgId={}", message.getMsgId());
        
        // 记录到数据库
        saveToDeadLetterTable(message);
        
        // 人工介入处理
        // 发送告警通知
        sendAlert(message);
    }
}

4. 定时消息(5.0+特性)

RocketMQ 5.0支持任意时间的定时消息。

java 复制代码
// 发送定时消息
Message<String> msg = MessageBuilder
    .withPayload(message)
    .setHeader("__STARTDELIVERTIME", System.currentTimeMillis() + 60000)
    .build();

rocketMQTemplate.send(topic, msg);

4. 最佳实践总结

消息设计

  1. 消息大小:单条消息不超过4MB
  2. 消息结构:使用JSON格式,便于解析
  3. 唯一标识:每条消息设置唯一key
  4. 业务字段:包含必要的业务字段
  5. 版本管理:消息结构升级做好版本兼容

错误处理

  1. 重试策略:合理设置重试次数和间隔
  2. 降级方案:准备降级方案应对故障
  3. 告警机制:关键异常及时告警
  4. 日志记录:记录详细的错误日志
  5. 故障恢复:制定完善的故障恢复流程

测试策略

  1. 单元测试:覆盖核心业务逻辑
  2. 集成测试:测试与RocketMQ的集成
  3. 压力测试:模拟高并发场景
  4. 故障测试:模拟各种故障场景
  5. 回归测试:版本升级后充分测试

5. 参考资源


结语

本文详细介绍了RocketMQ的核心概念、环境搭建、与Spring Boot的完整整合过程,包括普通消息、顺序消息、事务消息、延时消息等多种消息类型的实现。通过本文的学习,开发者可以快速掌握RocketMQ的使用方法,并在实际项目中应用。

RocketMQ作为一款成熟的消息中间件,在金融级场景下的可靠性表现尤为突出。在实际项目中,需要根据业务需求选择合适的消息类型和配置参数,同时做好监控、告警和容灾准备。

相关推荐
小北方城市网2 小时前
SpringBoot 集成 RabbitMQ 实战(消息队列):实现异步通信与系统解耦
java·spring boot·后端·spring·rabbitmq·mybatis·java-rabbitmq
indexsunny2 小时前
互联网大厂Java面试实战:Spring Boot与微服务在电商场景中的应用解析
java·数据库·spring boot·微服务·maven·flyway·电商
sunnyday04263 小时前
从混乱到清晰:Maven 依赖版本管理最佳实践
java·spring boot·后端·maven
未来龙皇小蓝3 小时前
策略模式:Spring Bean策略与枚举 Lambda策略
java·windows·spring boot·spring·策略模式
张乔243 小时前
spring boot项目中设置默认的方法实现
java·数据库·spring boot
小北方城市网3 小时前
SpringBoot 集成 Redis 实战(缓存与分布式锁):提升系统性能与并发能力
spring boot·python·rabbitmq·java-rabbitmq·数据库架构
韩立学长3 小时前
基于Springboot琴行学生课程信息管理系统2gt392wb(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
XXOOXRT3 小时前
基于SpringBoot的用户登录
java·spring boot·后端
齐 飞4 小时前
springboot整合shardingsphere-jdbc5.1.1-按月分表
数据库·spring boot