切换主题
10. 异步发送:大促时不要让消息发送阻塞主链路
业务增长
大促来了。下单接口每秒请求明显上涨。同步发送消息虽然比同步调用多个服务轻很多,但它仍然会等待发送结果。
产品只关心用户尽快看到“下单成功”。技术上要继续缩短主链路耗时。
这时可以考虑异步发送。
同步发送和异步发送
| 方式 | 特点 | 适合场景 |
|---|---|---|
| 同步发送 | 调用方等待发送结果 | 关键事件、低中等吞吐 |
| 异步发送 | 通过 future 或回调处理结果 | 高吞吐、对响应时间敏感 |
不是所有消息都要异步。支付成功这类关键事件,优先保证处理清晰。异步发送更适合大流量下的旁路事件。
异步发送普通消息
java
public CompletableFuture<SendReceipt> publishOrderCreatedAsync(OrderCreatedEvent event) {
CompletableFuture<SendReceipt> future = new CompletableFuture<>();
future.whenCompleteAsync((receipt, error) -> {
if (error != null) {
log.error("订单创建消息发送失败, orderId={}", event.orderId(), error);
messageOutboxRepository.markFailed(event.eventId(), error.getMessage());
return;
}
log.info("订单创建消息发送成功, orderId={}, messageId={}",
event.orderId(), receipt.getMessageId());
messageOutboxRepository.markSent(event.eventId(), receipt.getMessageId().toString());
});
return rocketMQClientTemplate.asyncSendNormalMessage(
"order-event-topic",
event,
future
);
}官方 2.3.4 RocketMQClientTemplate 提供了 asyncSendNormalMessage、asyncSendFifoMessage、asyncSendDelayMessage。
异步发送不等于不用管结果
异步只是“不在当前线程等待”。发送结果仍然要处理:
- 成功时记录 messageId。
- 失败时记录失败原因。
- 必要时进入本地 outbox 或补偿任务。
- 不要把异常静默吞掉。
小技巧
异步回调建议使用独立线程池。大促时默认线程池可能和业务线程互相影响。
java
ExecutorService callbackExecutor = Executors.newFixedThreadPool(8);
future.whenCompleteAsync((receipt, error) -> {
// 处理发送结果
}, callbackExecutor);线程池大小要结合机器资源、发送量和回调逻辑复杂度,不要盲目开大。
常见坑
异步发送后立刻认为成功。
future 还没完成,消息可能失败。回调里做重业务。
回调尽量只做状态记录,复杂补偿交给后台任务。关键一致性场景滥用异步。
支付消息优先考虑事务消息,而不是简单异步发送。
练习题
- 下单成功通知适合异步发送吗?
- 支付成功履约消息适合简单异步发送吗?
- 异步发送失败后,最少要记录哪些信息?
参考答案
- 适合,但要记录发送结果和补偿。
- 不适合简单异步。更适合事务消息或 outbox 方案。
eventId、业务主键、topic、payload 摘要、失败原因、重试次数、发生时间。
来源
- 官方 producer sample:https://github.com/apache/rocketmq-spring/blob/rocketmq-spring-all-2.3.4/rocketmq-v5-client-spring-boot-samples/rocketmq-v5-client-producer-demo/src/main/java/org/apache/rocketmq/samples/springboot/ClientProducerApplication.java
RocketMQClientTemplate#asyncSend*:https://github.com/apache/rocketmq-spring/blob/rocketmq-spring-all-2.3.4/rocketmq-v5-client-spring-boot/src/main/java/org/apache/rocketmq/client/core/RocketMQClientTemplate.java