Spring Boot集成RocketMQ

目录

1.依赖

2.配置

3.同步消息

4.异步消息

5.单向消息

6.顺序消息

7.事务消息

8.报错处理

[sendDefaultImpl call timeout](#sendDefaultImpl call timeout)

延时消息不生效的天坑


1.依赖

复制代码
<!-- pom.xml -->
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>2.2.3</version>
</dependency>

2.配置

不管是生产者还是消费者端,都要配置好nameserver的地址,这样才找的到broker。消费者端还要配置好当前消费者属于哪个消费者组,rocketmq中同一个messagequeue,一个消费者组只能消费一次,这是默认的规则。

复制代码
# application.yml
rocketmq:
  name-server: 127.0.0.1:9876 #nameserver地址,支持用逗号分隔多个
  producer:
    group: my-producer-group #消费者组

3.同步消息

Sprong Boot中有自己的一套标准,去操作诸如数据库、缓存、中间件之类的第三方组件都喜欢通过一个XXXTemplate去操作,因此第三方组件自己封装starter的时候也会按照这种方式去封装,将API操作都通过一个XXXTemplate来,操作MQ也不例外,操作rocketmq是通过RocketMQTemplate去。

java 复制代码
@RestController
public class SyncProducerController {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
​
    @GetMapping("/sendSync")
    public String sendSyncMessage() {
        Message<String> message = MessageBuilder.withPayload("同步消息内容")
                .setHeader(MessageConst.PROPERTY_KEYS, "ORDER_20231109001")
                .build();
        
        SendResult sendResult = rocketMQTemplate.syncSend("SYNC_TOPIC", message);
        return "发送成功,消息ID:" + sendResult.getMsgId();
    }
}

消费者都是通过Listener来消费消息

java 复制代码
@Service
@RocketMQMessageListener(
    topic = "SYNC_TOPIC",
    consumerGroup = "sync-consumer-group",
    selectorType = SelectorType.TAG,
    selectorExpression = "*"
)
public class SyncConsumer implements RocketMQListener<String> {
    @Override
    public void onMessage(String message) {
        System.out.println("收到同步消息: " + message);
    }
}

4.异步消息

异步消息因为涉及要接收一个异步响应,所以要注册一个回调函数,异步响应回来之后触发这个异步响应:

java 复制代码
public class AsyncProducerService {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
​
    public void sendAsync() {
        Message<String> message = MessageBuilder.withPayload("异步消息内容")
                .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, "3") // 延迟级别
                .build();
​
        rocketMQTemplate.asyncSend("ASYNC_TOPIC", message, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("异步发送成功: " + sendResult.getMsgId());
            }
​
            @Override
            public void onException(Throwable e) {
                System.err.println("异步发送失败: " + e.getMessage());
            }
        });
    }
}

5.单向消息

java 复制代码
@RestController
public class OnewayController {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
​
    @GetMapping("/sendOneway")
    public String sendOneway() {
        rocketMQTemplate.sendOneWay("ONEWAY_TOPIC", 
            MessageBuilder.withPayload("日志消息").build());
        return "单向消息已发送";
    }
}

6.顺序消息

顺序消息说白了只要是同一个messagequeue中的消息都是顺序的,所以要做成顺序消息也就是走同一个queueId的messagequeue中,也就是在发送消息的时候指定queueId。

java 复制代码
// 订单服务生产者
public class OrderProducer {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
​
    // 发送顺序消息(以订单ID为路由键)
    public void sendOrderMessage(String orderId, String operation) {
        // 构建消息体
        Message<String> message = MessageBuilder.withPayload(operation)
                .setHeader("orderId", orderId)
                .build();
​
        // 关键代码:发送顺序消息
        rocketMQTemplate.syncSendOrderly(
            "ORDER_TOPIC", 
            message,
            orderId, // 使用订单ID作为队列选择键
            3000     // 超时时间
        );
    }
}
复制代码
消费者也可以指定queueId的messagequeue中去消费消息。
java 复制代码
@Service
@RocketMQMessageListener(
    topic = "ORDER_TOPIC",
    consumerGroup = "order_consumer_group",
    consumeMode = ConsumeMode.ORDERLY,  // 必须设置为顺序模式
    messageModel = MessageModel.CLUSTERING,
    selectorExpression = "*",
    consumeThreadNumber = 4  // 线程数必须 ≤ 队列数
)
public class OrderConsumer implements RocketMQListener<MessageExt> {
​
    private final ConcurrentHashMap<String, Boolean> processingOrders = new ConcurrentHashMap<>();
​
    @Override
    public void onMessage(MessageExt message) {
        String orderId = message.getProperty("orderId");
        String body = new String(message.getBody(), StandardCharsets.UTF_8);
​
        try {
            // 1. 检查订单是否正在处理(防并发)
            if (processingOrders.putIfAbsent(orderId, true) != null) {
                throw new RuntimeException("存在并发的订单处理: " + orderId);
            }
​
            // 2. 顺序消费逻辑
            processOrder(orderId, body);
​
        } catch (Exception e) {
            // 3. 消费失败时挂起当前队列
            throw new RuntimeException("消费失败,触发队列重试", e);
        } finally {
            // 4. 移除处理标记
            processingOrders.remove(orderId);
        }
    }
​
    private void processOrder(String orderId, String operation) {
        // 实际业务处理(必须幂等)
        System.out.printf("处理订单 %s 的操作: %s%n", orderId, operation);
    }
}

7.事务消息

java 复制代码
// 事务消息生产者
public class TransactionProducer {
    private final RocketMQTemplate rocketMQTemplate;

    public void sendTransactionMessage(String messageBody) {
        // 发送半事务消息
        Message<String> message = MessageBuilder.withPayload(messageBody)
                .setHeader(RocketMQHeaders.TRANSACTION_ID, UUID.randomUUID().toString())
                .build();
        
        rocketMQTemplate.sendMessageInTransaction(
            "tx_topic", 
            message, 
            null
        );
    }
}
java 复制代码
// 事务监听器(核心实现)
@Slf4j
@RocketMQTransactionListener
public class TransactionListenerImpl implements RocketMQLocalTransactionListener {
    
    // 执行本地事务
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        try {
            // 这里执行本地数据库操作等业务逻辑
            // 如果执行成功,返回COMMIT
            return RocketMQLocalTransactionState.COMMIT;
            
        } catch (Exception e) {
            log.error("本地事务执行失败", e);
            return RocketMQLocalTransactionState.ROLLBACK;
        }
    }

    // 事务回查
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
        // 根据业务状态检查事务是否成功
        // 例如:查询数据库判断事务是否完成
        return RocketMQLocalTransactionState.COMMIT;
    }
}

8.报错处理

sendDefaultImpl call timeout

RocketMq提示RemotingTooMuchRequestException: sendDefaultImpl call timeout:

延时消息不生效的天坑

只能用下面这种方式延迟消息才会延迟投递:

用下面这种设置Header的方式不会生效!!延时消息会被立即投递!!!

java 复制代码
Message<Order> delayedMessage = MessageBuilder.withPayload(order)
        .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, "16")
        .build();
相关推荐
纯洁的小魔鬼28 分钟前
Springboot 配置 doris 连接
spring boot·doris·连接池
c_zyer29 分钟前
Mermaid流程图可视化系统:基于Spring Boot与Node.js的三层架构实现
spring boot·node.js·流程图·mermaid
lang201509282 小时前
使用 Docker 部署 Apache RocketMQ
docker·apache·rocketmq
_码农121382 小时前
spring boot + mybatis + mysql 只有一个实体类的demo
spring boot·mysql·mybatis
c_zyer3 小时前
FreeSWITCH与Java交互实战:从EslEvent解析到Spring Boot生态整合的全指南
spring boot·netty·freeswitch·eslevent
郝学胜-神的一滴3 小时前
Spring Boot Actuator 保姆级教程
java·开发语言·spring boot·后端·程序人生
lang201509284 小时前
Apache RocketMQ中 Consumer Group(消费者组)的详细说明
apache·rocketmq
斜月4 小时前
Springboot 项目加解密的那些事儿
spring boot·后端
草莓爱芒果4 小时前
Spring Boot中使用Bouncy Castle实现SM2国密算法(与前端JS加密交互)
java·spring boot·算法
汤姆yu6 小时前
基于springboot的快递分拣管理系统
java·spring boot·后端