
1. 项目背景与核心需求在餐饮外卖系统中订单状态流转的实时性和准确性直接影响用户体验和商家运营效率。传统轮询方式不仅浪费服务器资源还难以保证时效性。我们基于Spring Cloud架构开发的苍穹外卖系统需要解决以下三个核心问题订单超时自动取消15分钟未支付新订单实时推送商家端用户催单提醒及时处理2. 技术方案选型2.1 定时任务实现对比方案优点缺点适用场景Spring Scheduled简单易用零配置单机运行不支持动态调整小型单体应用Quartz分布式支持动态调度配置复杂需要额外存储企业级复杂调度XXL-JOB可视化管控弹性扩容需要独立部署调度中心微服务架构Elastic-Job分布式协调故障转移依赖Zookeeper学习成本高高可用要求场景最终选择Quartz方案因其与Spring Boot生态无缝集成支持集群环境下避免重复执行可通过数据库持久化任务状态动态调整cron表达式无需重启2.2 实时通知方案WebSocket相比传统轮询的优势全双工通信服务端可主动推送单个TCP连接持续复用低延迟毫秒级响应减少无效请求节约70%带宽3. 核心实现细节3.1 Quartz集群配置# application-quartz.yml spring: quartz: job-store-type: jdbc jdbc: initialize-schema: never properties: org.quartz.scheduler.instanceName: ClusterQuartzScheduler org.quartz.scheduler.instanceId: AUTO org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.tablePrefix: QRTZ_ org.quartz.jobStore.isClustered: true org.quartz.jobStore.clusterCheckinInterval: 20000 org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount: 10关键配置说明clusterCheckinInterval节点心跳间隔(ms)isClusteredtrue 启用集群模式tablePrefix 避免与业务表冲突3.2 订单状态定时任务Slf4j public class OrderStatusJob implements Job { Override public void execute(JobExecutionContext context) { ListOrder unpaidOrders orderMapper.selectUnpaidOrders(15); unpaidOrders.forEach(order - { order.setStatus(OrderStatus.CANCELLED); order.setCancelReason(超时未支付); orderMapper.updateById(order); // 释放库存 inventoryService.unlock(order.getItems()); }); } }执行策略cron表达式0 */1 * * * ?每分钟扫描幂等设计通过version字段乐观锁控制性能优化分批处理每批100条3.3 WebSocket消息推送ServerEndpoint(/ws/notification/{shopId}) Component public class OrderWebSocket { private static final MapLong, Session sessions new ConcurrentHashMap(); OnOpen public void onOpen(Session session, PathParam(shopId) Long shopId) { sessions.put(shopId, session); } public static void sendNewOrder(Long shopId, OrderVO order) { Session session sessions.get(shopId); if(session ! null) { session.getAsyncRemote().sendText(JSON.toJSONString(order)); } } }消息协议设计{ type: NEW_ORDER|REMINDER, data: { orderId: 202308011001, createTime: 2023-08-01 10:00:00, totalAmount: 38.50 } }4. 生产环境问题排查4.1 定时任务常见故障现象可能原因解决方案任务重复执行集群节点时间不同步部署NTP时间同步服务任务堆积单线程阻塞增加线程池大小数据库连接泄漏Job中未关闭数据库连接使用try-with-resources错过触发时间服务器重启未持久化配置持久化存储4.2 WebSocket优化实践心跳检测机制// 客户端每30秒发送ping setInterval(() { ws.send({type:ping}); }, 30000); // 服务端配置 Bean public ServletServerContainerFactoryBean createWebSocketContainer() { ServletServerContainerFactoryBean container new ServletServerContainerFactoryBean(); container.setMaxSessionIdleTimeout(60000L); return container; }断线重连策略let reconnectAttempts 0; function connect() { ws new WebSocket(url); ws.onclose function() { let timeout Math.min(1000 * Math.pow(2, reconnectAttempts), 30000); setTimeout(connect, timeout); reconnectAttempts; }; }5. 性能压测数据5.1 Quartz集群表现节点数QPS平均延迟CPU使用率1120045ms68%2230051ms72%3340053ms75%5.2 WebSocket连接容量连接数内存占用消息延迟带宽消耗10001.2GB12ms3Mbps50004.8GB18ms15Mbps100009.5GB25ms30Mbps优化建议超过5000连接时考虑分片部署使用Protobuf替代JSON可减少40%带宽开启GZIP压缩进一步降低传输量6. 扩展设计思考6.1 状态机模式优化订单流转public enum OrderState { INIT { Override public void pay(Order order) { if(validatePayment(order)) { order.setState(PAID); } } }, PAID { Override public void deliver(Order order) { order.setState(DELIVERING); } }; // 其他状态和方法... }优势集中管理状态转换逻辑避免if-else分支污染业务代码新增状态只需扩展枚举6.2 分布式事务补偿方案对于库存扣减与订单创建的一致性TCC模式实现Transactional public void tryCreateOrder(OrderDTO dto) { // 1. 冻结库存 inventoryService.freeze(dto.getItems()); // 2. 创建待支付订单 orderMapper.insert(dto.toOrder()); } Transactional public void confirmCreateOrder(Long orderId) { Order order getById(orderId); order.setStatus(PAID); updateById(order); } Transactional public void cancelCreateOrder(Long orderId) { Order order getById(orderId); inventoryService.unfreeze(order.getItems()); deleteById(orderId); }定时任务兜底-- 每小时扫描悬挂事务 SELECT * FROM t_order WHERE status TRY AND create_time NOW() - INTERVAL 1 HOUR;7. 监控与告警配置7.1 Prometheus监控指标# application.yml management: endpoints: web: exposure: include: health,info,metrics,prometheus metrics: tags: application: ${spring.application.name}关键指标quartz_jobs_executed_total任务执行次数websocket_connections_active当前连接数order_status_changes_total状态变更统计7.2 Grafana看板配置-- 订单状态分布 SELECT status, COUNT(*) as count FROM t_order GROUP BY status; -- WebSocket消息速率 SELECT rate(websocket_messages_sent_total[1m]) FROM metrics;告警规则示例alert: HighOrderCancelRate expr: rate(order_status_changes_total{statuscancelled}[5m]) 0.1 for: 10m labels: severity: warning annotations: summary: 高订单取消率 ({{ $value }})8. 实际部署经验Quartz集群注意事项各节点必须配置相同的时间源数据库连接池建议使用HikariCP避免在Job中执行长时间阻塞操作WebSocket生产建议Nginx配置需要添加proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_read_timeout 86400s;对于移动端建议实现退后台重连机制重要消息需要添加客户端ACK确认订单表设计优化CREATE TABLE t_order ( id BIGINT PRIMARY KEY, status ENUM(INIT,PAID,DELIVERING,COMPLETED,CANCELLED), user_id BIGINT, shop_id BIGINT, amount DECIMAL(10,2), INDEX idx_shop_status (shop_id, status), INDEX idx_user_createtime (user_id, create_time) ) ENGINEInnoDB;