1. 项目概述:外卖系统订单状态管理实战
最近在重构一个外卖系统的订单状态管理模块,核心要解决三个业务痛点:超时未支付订单的自动关闭、商家端实时来单提醒、用户催单的即时通知。这套机制看似简单,实际开发中涉及到定时任务调度、WebSocket实时通信、状态机设计等多个技术点的综合运用。下面我就把从零实现的完整过程拆解开来,重点分享那些文档里不会写的实战细节。
2. 技术选型与核心组件
2.1 Spring Task定时任务
订单超时处理需要可靠的定时任务机制。对比了Quartz和Spring Task后,最终选择轻量级的Spring Task,主要基于以下考量:
- 依赖成本:Spring Task直接集成在Spring框架中,无需额外引入依赖
- 配置简便:通过@Scheduled注解即可快速定义任务
- 够用原则:我们的业务场景不需要分布式调度等复杂特性
关键配置示例:
java复制@Configuration
@EnableScheduling
public class TaskConfig {
// 线程池配置避免任务阻塞
@Bean(destroyMethod = "shutdown")
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5);
scheduler.setThreadNamePrefix("order-task-");
scheduler.setAwaitTerminationSeconds(60);
return scheduler;
}
}
2.2 WebSocket实时通信
商家端需要实时接收新订单和催单通知,传统HTTP轮询方案会有明显延迟且服务端压力大。WebSocket的全双工通信特性完美契合这个场景,具体实现时需要注意:
- 连接保持:需要处理网络闪断后的自动重连
- 消息确认:重要业务消息需要客户端确认机制
- 会话管理:维护商家ID与WebSocket Session的映射关系
3. 核心业务实现细节
3.1 订单状态定时处理
超时订单关闭是典型的定时任务场景,但实际开发中会遇到几个关键问题:
- 时间精度控制:
java复制@Scheduled(cron = "0 */5 * * * ?") // 每5分钟执行一次
public void processTimeoutOrders() {
// 查询待支付超时订单
List<Order> orders = orderMapper.selectTimeoutOrders(
LocalDateTime.now().minusMinutes(30)); // 30分钟未支付
orders.forEach(order -> {
order.setStatus(OrderStatus.CLOSED);
order.setCancelReason("超时未支付");
orderMapper.update(order);
// 返还库存
refundStock(order);
});
}
- 批量处理优化:当订单量较大时需要分页查询,避免内存溢出
- 幂等设计:定时任务可能重复触发,需要保证多次执行结果一致
踩坑提醒:数据库查询一定要加合适的索引,特别是status和create_time的联合索引,否则高峰期可能拖垮数据库。
3.2 来单提醒实现
支付成功后的实时通知流程:
- 支付回调处理:
java复制@Transactional
public void paySuccess(String orderId) {
// 更新订单状态
Order order = getOrder(orderId);
order.setStatus(OrderStatus.PAID);
updateOrder(order);
// 发送WebSocket通知
Map<String, Object> content = new HashMap<>();
content.put("type", "NEW_ORDER");
content.put("orderId", orderId);
websocketServer.sendToShop(
order.getShopId(),
JSON.toJSONString(content));
}
- 前端处理逻辑:
javascript复制socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if(data.type === 'NEW_ORDER') {
showNotification(`新订单: ${data.orderId}`);
playSound('/assets/new-order.mp3');
}
};
- 可靠性保障:
- 消息失败重试机制
- 离线消息缓存(Redis存储)
- 消息消费确认
3.3 用户催单功能
催单流程的核心在于状态校验和限流控制:
java复制public void remindOrder(String orderId, Long userId) {
// 状态校验
Order order = checkOrderValid(orderId, userId);
// 限流控制(同一订单30分钟内只能催单一次)
String lockKey = "remind:" + orderId;
if (redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 30, MINUTES)) {
// 构造催单消息
RemindMessage message = buildRemindMessage(order);
// 发送给商家
websocketServer.sendToShop(
order.getShopId(),
JSON.toJSONString(message));
// 记录操作日志
logRemindAction(orderId, userId);
} else {
throw new BusinessException("请勿频繁催单");
}
}
4. 性能优化与问题排查
4.1 定时任务优化方案
当订单量达到10万+时,最初的批量查询方案出现了性能问题。优化后的方案:
- 分片处理:按店铺ID分片,避免全表扫描
- 游标查询:改用基于ID范围的游标方式
- 异步处理:耗时操作放入线程池
优化后的代码结构:
java复制@Scheduled(cron = "0 */5 * * * ?")
public void processTimeoutOrders() {
Long maxId = orderMapper.getMaxId();
Long batchSize = 1000L;
for (Long from = 1L; from <= maxId; from += batchSize) {
Long to = Math.min(from + batchSize - 1, maxId);
taskExecutor.execute(() -> {
processOrderBatch(from, to);
});
}
}
4.2 WebSocket常见问题
- 连接不稳定:
- 心跳机制保持连接活跃
- 自动重连策略(指数退避算法)
- 消息堆积:
- 设置合理的消息过期时间
- 重要消息持久化存储
- 集群部署:
- 使用Redis Pub/Sub同步各节点会话
- 会话信息集中存储
5. 监控与日志设计
完善的监控体系能快速定位问题:
- 定时任务监控:
java复制@Around("@annotation(scheduled)")
public Object monitorScheduled(ProceedingJoinPoint pjp) {
String taskName = pjp.getSignature().getName();
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
long cost = System.currentTimeMillis() - start;
metrics.recordTaskExecution(taskName, cost);
}
}
- WebSocket关键指标:
- 在线连接数
- 消息投递成功率
- 平均延迟
- 业务日志:
- 订单状态变更流水
- 催单操作记录
- 通知发送日志
6. 安全防护措施
- WebSocket认证:
java复制@Override
public void afterConnectionEstablished(WebSocketSession session) {
String token = session.getHandshakeHeaders().getFirst("Authorization");
if (!tokenService.validateToken(token)) {
session.close(CloseStatus.NOT_ACCEPTABLE);
return;
}
// 存储认证后的会话
}
- 防刷策略:
- 催单频率限制
- 异常IP检测
- 行为验证码
- 数据安全:
- 敏感字段加密
- 日志脱敏处理
- SQL注入防护
这套系统上线后,商家的订单处理效率提升了40%,用户投诉率下降了25%。最大的收获是认识到:看似简单的业务功能,背后需要各种技术点的有机组合,而系统稳定性的关键往往在于那些异常情况的处理。