一、异步处理的必要性
1. 同步阻塞的痛点
-
线程资源浪费:同步请求中,Tomcat 线程需等待业务逻辑执行完毕(如数据库查询、远程调用),导致线程阻塞,并发能力受限。
-
响应延迟:耗时操作(如报表生成、消息推送)拖慢主线程,用户等待时间过长。
-
资源耗尽风险:高并发场景下,大量线程阻塞可能导致线程池耗尽,服务不可用。
2. 异步处理的优势
指标 | 同步处理 | 异步处理 |
---|---|---|
线程占用 | 全程占用(阻塞) | 仅触发时占用(非阻塞) |
吞吐量 | 低(受限于线程数) | 高(线程复用) |
响应速度 | 慢(等待所有操作完成) | 快(立即返回中间结果) |
适用场景 | 强一致性业务(支付) | 非核心任务(日志、通知) |
二、异步处理五大实现方案
1. 线程池(@Async)
适用场景:短耗时任务(<1秒),如日志记录、本地计算。
配置示例:
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10); // CPU密集型:N+1,IO密集型:2N
executor.setMaxPoolSize(100);
executor.setQueueCapacity(1000);
executor.setThreadNamePrefix("Async-Executor-");
return executor;
}
}
@Service
public class NotificationService {
@Async // 使用线程池异步执行
public void sendSms(String phone) {
smsClient.send(phone, "您的订单已发货");
}
}
优势:简单易用,减少主线程等待时间。
2. DeferredResult(长轮询)
适用场景:需主动推送结果的业务(如订单状态查询)。
工作原理:
sequenceDiagram
用户->>服务端: 发起请求
服务端->>内存队列: 存储DeferredResult
服务端->>用户: 立即返回(不阻塞)
异步线程-->>服务端: setResult(订单状态)
服务端->>用户: 推送结果
代码实现:
@RestController
public class OrderController {
private Map<String, DeferredResult<OrderStatus>> resultMap = new ConcurrentHashMap<>();
@GetMapping("/order/status/{orderId}")
public DeferredResult<OrderStatus> getStatus(@PathVariable String orderId) {
DeferredResult<OrderStatus> result = new DeferredResult<>(30_000L); // 30秒超时
resultMap.put(orderId, result);
result.onCompletion(() -> resultMap.remove(orderId));
return result;
}
// 异步更新订单状态
public void updateOrderStatus(String orderId, OrderStatus status) {
DeferredResult<OrderStatus> result = resultMap.get(orderId);
if (result != null) result.setResult(status);
}
}
优势:支持长连接,减少客户端轮询。
3. 消息队列(RabbitMQ)
适用场景:跨服务解耦、流量削峰(如订单创建后的积分发放)。
可靠性设计:
-
生产者确认:确保消息成功投递到队列。
-
消费者ACK:手动确认消息处理完毕,失败则重试或进入死信队列。
代码示例:
@Bean
public Queue orderQueue() {
return new Queue("order.queue", true); // 持久化队列
}
@Component
public class OrderMessageSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendCreateOrder(Order order) {
rabbitTemplate.convertAndSend("order.exchange", "order.create", order);
}
}
@Component
@RabbitListener(queues = "order.queue")
public class OrderMessageHandler {
public void handleCreateOrder(Order order) {
pointService.addPoints(order.getUserId(), order.getAmount()); // 异步发放积分
}
}
4. 事件驱动(Spring Events)
适用场景:应用内部模块解耦(如订单创建后触发日志记录和库存更新)。
实现流程:
// 1. 定义领域事件
public class OrderCreatedEvent extends ApplicationEvent {
private Order order;
public OrderCreatedEvent(Object source, Order order) {
super(source);
this.order = order;
}
}
// 2. 发布事件
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
eventPublisher.publishEvent(new OrderCreatedEvent(this, order));
}
}
// 3. 监听事件(异步执行)
@Component
public class OrderEventHandler {
@EventListener
@Async
public void handleOrderCreated(OrderCreatedEvent event) {
logService.record("订单创建", event.getOrder());
inventoryService.updateStock(event.getOrder().getItems());
}
}
5. 响应式编程(WebFlux)
适用场景:高并发IO密集型服务(如API网关、实时数据流)。
核心组件:
-
Mono
:单数据流(如根据ID查询用户)。 -
Flux
:多数据流(如分页查询用户列表)。代码示例:
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductReactiveRepository repository;
@GetMapping("/{id}")
public Mono<Product> getProduct(@PathVariable String id) {
return repository.findById(id)
.timeout(Duration.ofMillis(500)) // 超时控制
.onErrorResume(e -> Mono.just(getFallbackProduct()));
}
@GetMapping
public Flux<Product> listProducts() {
return repository.findAll().delayElements(Duration.ofMillis(10));
}
}
性能优势:非阻塞IO模型,单线程支持万级并发。
三、异步处理的避坑指南
1. 禁止异步化的场景
场景 | 原因 | 正确实践 |
---|---|---|
支付核心逻辑 | 需即时返回结果,保证原子性 | 同步处理 + 数据库事务 |
库存扣减 | 避免超卖(强一致性) | 同步锁库存 + 事务控制 |
用户登录 | 需即时返回Token | 同步验证 + 生成Token |
2. 常见问题与解决方案
-
事务边界问题:
@Transactional public void updateProfile(User user) { userDao.update(user); // 同步更新 // 事务提交后异步记录日志 TransactionSynchronizationManager.registerSynchronization( new TransactionSynchronization() { @Override public void afterCommit() { asyncService.logUpdate(user); } } ); }
-
超时控制:
-
DeferredResult
设置超时时间(如30秒)。 -
RabbitMQ 配置死信队列处理积压消息。
-
四、企业级实战:订单系统异步化改造
1. 同步流程痛点
@PostMapping("/order")
public Result createOrderSync(OrderDTO dto) {
validate(dto); // 校验(10ms)
lockStock(dto); // 锁库存(50ms)
saveOrder(dto); // 存订单(100ms)
sendSms(dto); // 发短信(300ms)
return Result.success(); // 总耗时460ms
}
问题:短信发送阻塞主线程,并发量低。
2. 异步优化方案
@PostMapping("/order")
public Result createOrderAsync(OrderDTO dto) {
validate(dto); // 同步核心步骤
lockStock(dto);
saveOrder(dto);
// 非核心操作异步化
asyncService.asyncSendSms(dto); // 异步发短信
return Result.success("订单处理中"); // 总耗时160ms
}
效果:
-
响应时间:460ms → 160ms
-
吞吐量:100 TPS → 1500 TPS
-
Tomcat 线程利用率:90% → 30%
五、异步方案选型对比
方案 | 适用场景 | 吞吐量 | 可靠性 | 复杂度 |
---|---|---|---|---|
线程池(@Async) | 短耗时任务(日志) | 高 | 中(无重试) | 低 |
DeferredResult | 长轮询(订单状态) | 中 | 高 | 中 |
消息队列(RabbitMQ) | 跨服务解耦(积分发放) | 极高 | 高(持久化) | 高 |
事件驱动(Spring Events) | 模块解耦(日志+库存) | 高 | 中 | 中 |
WebFlux | IO密集型(API网关) | 极高 | 高 | 高 |
总结
-
核心思想:异步的本质是 “将阻塞操作与主线程解耦”,通过释放线程资源提升并发能力。
-
选型铁律:
-
强一致性 → 同步(支付、库存)
-
弱一致性 → 异步(日志、通知)
-
-
避坑关键:
-
事务边界用
TransactionSynchronizationManager
控制。 -
消息队列必须配置死信队列和重试机制。
-
-
性能压测:
-
线程池参数动态调整(
corePoolSize = 2 * CPU核心数
)。 -
响应式编程适用于 >5000 TPS 的高并发场景。
-
“异步不是银弹,而是架构师手中的手术刀——精准切割阻塞点,方能释放系统潜力。”
动手实践:
用
@Async
改造日志记录功能,对比同步/异步的吞吐量差异。基于 RabbitMQ 实现订单创建与积分发放的解耦。
使用 WebFlux 重写商品查询接口,模拟 10K 并发压测。
扩展学习: