跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Java大前端java

Spring Task 与 WebSocket 实战:苍穹外卖订单实时通知方案

Spring Task 提供轻量级定时任务调度,适合处理订单超时关闭、数据统计等场景;WebSocket 实现全双工通信,解决 HTTP 轮询延迟问题。结合苍穹外卖案例,演示如何通过 @Scheduled 注解配置任务,以及利用 WebSocket 推送来单提醒、催单和状态变更消息。内容涵盖服务端线程池优化、Cron 表达式详解及前后端完整交互代码,助力构建高实时性的订单通知系统。

SecGuard发布于 2026/3/30更新于 2026/6/116 浏览
Spring Task 与 WebSocket 实战:苍穹外卖订单实时通知方案

Spring Task 与 WebSocket 实战:苍穹外卖订单实时通知方案

在苍穹外卖项目的开发中,订单的定时处理和实时通知是提升用户体验的关键环节。为了实现来单提醒、催单以及订单状态更新等功能,我们需要引入 Spring Task 进行定时调度,并利用 WebSocket 实现服务端主动推送。

Spring Task 入门与实践

什么是 Spring Task

Spring Task 是 Spring 框架内置的轻量级定时任务调度框架。相比 Quartz,它配置更简单,无需依赖第三方库,非常适合单体应用中的常规定时任务场景。

核心优势包括零侵入性(基于注解)、轻量级以及对 Spring 生态的无缝集成(支持依赖注入和事务管理)。

快速上手

1. 开启调度支持

在启动类或配置类上添加 @EnableScheduling 注解即可激活定时任务功能:

@SpringBootApplication
@EnableScheduling
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
2. 编写任务类

创建一个组件,使用 @Scheduled 注解标记需要定时执行的方法。支持三种调度模式:

  • 固定延迟 (fixedDelay):上次任务结束后等待指定时间再执行下一次。
  • 固定频率 (fixedRate):每隔指定时间执行一次,不等待上次任务完成。
  • Cron 表达式 (cron):灵活定义执行时间点。
@Component
public class ScheduledTasks {
    private static final Logger logger = LoggerFactory.getLogger(ScheduledTasks.class);

    /**
     * 固定延迟:上次执行完成后间隔 5 秒执行
     */
    @Scheduled(fixedDelay = 5000)
    public void taskWithFixedDelay() {
        logger.info("FixedDelay Task - 当前时间:{}", System.currentTimeMillis());
    }

    /**
     * Cron 表达式:每分钟的第 10 秒执行
     */
    @Scheduled(cron = "10 * * * * ?")
    public void taskWithCron() {
        logger.info("Cron Task - 当前时间:{}", System.currentTimeMillis());
    }
}

Cron 表达式详解

Cron 表达式格式为:秒 分 时 日 月 周 [年]。

位置字段取值范围特殊字符
1秒0-59, - * /
2分0-59, - * /
3时0-23, - * /
4日1-31, - * ? / L W
5月1-12, - * /
6周1-7, - * ? / L #

常用示例:

// 每天凌晨 2 点执行
@Scheduled(cron = "0 0 2 * * ?")

// 每 10 分钟执行一次
@Scheduled(cron = "0 */10 * * * ?")

// 工作日上午 9 点执行
@Scheduled(cron = "0 0 9 * * MON-FRI")

线程池配置(重要)

默认情况下,Spring Task 使用单线程执行所有任务。如果某个任务耗时过长,会阻塞后续任务。生产环境建议配置异步线程池:

@Configuration
@EnableScheduling
public class SchedulingConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 设置线程池大小为 10
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
    }
}

或者在 application.yml 中配置:

spring:
  task:
    scheduling:
      pool:
        size: 10
        thread-name-prefix: my-scheduler-

最佳实践

  1. 避免阻塞:耗时操作应放入异步线程或 CompletableFuture 中执行。
  2. 异常处理:定时任务中的异常不会自动打印到控制台,务必捕获并记录日志。
  3. 动态配置:可通过配置文件动态修改 Cron 表达式,便于运维调整。

WebSocket 实时通信

HTTP 协议是单向的,客户端必须主动请求才能获取数据。对于订单提醒等场景,轮询效率低且延迟高。WebSocket 提供了全双工通信能力,允许服务器主动向客户端推送数据。

服务端实现

1. 引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2. 配置类

需要注册 ServerEndpointExporter 以自动扫描 @ServerEndpoint 注解的类:

@Configuration
public class WebSocketConfiguration {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
3. 核心业务逻辑

使用 ConcurrentHashMap 存储在线会话,支持单发和群发:

@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {
    private static ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();
    private Session session;
    private String sid;

    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        this.session = session;
        this.sid = sid;
        sessionMap.put(sid, session);
        System.out.println("【连接】用户 " + sid + " 已连接,当前在线人数:" + sessionMap.size());
        sendMessage(sid, "欢迎 " + sid + " 连接成功!");
    }

    @OnMessage
    public void onMessage(String message, @PathParam("sid") String sid) {
        System.out.println("【消息】用户 " + sid + " 发送:" + message);
        if ("ping".equals(message)) {
            sendMessage(sid, "pong");
        } else {
            sendToAll(sid + " 说:" + message);
        }
    }

    @OnClose
    public void onClose(@PathParam("sid") String sid) {
        sessionMap.remove(sid);
        System.out.println("【关闭】用户 " + sid + " 已断开");
        sendToAll("用户 " + sid + " 离开了聊天室");
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    /**
     * 发送消息给指定用户
     */
    public void sendMessage(String sid, String message) {
        Session s = sessionMap.get(sid);
        if (s != null && s.isOpen()) {
            try {
                s.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 群发消息
     */
    public void sendToAll(String message) {
        for (String key : sessionMap.keySet()) {
            sendMessage(key, message);
        }
    }
}

前端对接

浏览器端通过 JavaScript 建立连接,监听 onmessage 事件接收推送:

let ws = null;
const userId = 'user_' + Math.random().toString(36).substr(2, 6);

function connect() {
    const wsUrl = `ws://localhost:8080/ws/${userId}`;
    ws = new WebSocket(wsUrl);

    ws.onopen = function() {
        console.log('WebSocket 连接成功');
    };

    ws.onmessage = function(event) {
        console.log('收到消息:', event.data);
        // 解析 JSON 并更新 UI
        const data = JSON.parse(event.data);
        handlePushMessage(data);
    };

    ws.onclose = function() {
        console.log('连接已断开');
    };
}

connect();

苍穹外卖场景整合

无支付场景适配

若项目未接入微信支付,下单时需直接跳过待付款状态,将订单状态设为'待接单':

order.setStatus(2); // 待接单
order.setPayStatus(1); // 已支付

这样后续的 WebSocket 推送逻辑可直接基于该状态流转。

来单提醒实现

当用户提交订单后,后端通过 WebSocket 向商家端推送新订单信息:

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private WebSocketServer webSocketServer;

    @Override
    @Transactional
    public void submitOrder(OrderSubmitDTO orderSubmitDTO) {
        // 1. 保存订单
        Orders order = new Orders();
        // ... 设置属性 ...
        orderMapper.insert(order);

        // 2. 构建推送消息
        Map<String, Object> pushMessage = new HashMap<>();
        pushMessage.put("type", "NEW_ORDER");
        pushMessage.put("data", order);
        pushMessage.put("timestamp", System.currentTimeMillis());
        
        String jsonMessage = JSON.toJSONString(pushMessage);
        
        // 3. 发送给对应商家
        // 实际场景中需根据订单中的 shopId 匹配对应的 sessionId
        webSocketServer.sendToClient("shop_001", jsonMessage);
    }
}

催单功能

用户点击催单按钮时,后端查询订单并推送提醒消息:

@Override
public void remindOrder(Long orderId) {
    Orders order = orderMapper.getById(orderId);
    if (order == null || order.getStatus() != Orders.TO_BE_CONFIRMED) {
        throw new BusinessException("当前订单不支持催单");
    }

    Map<String, Object> remindMessage = new HashMap<>();
    remindMessage.put("type", "REMINDER");
    remindMessage.put("data", Map.of(
        "orderId", order.getId(),
        "orderNumber", order.getNumber()
    ));
    
    String jsonMessage = JSON.toJSONString(remindMessage);
    webSocketServer.sendToClient("shop_001", jsonMessage);
}

定时统计数据推送

利用 Spring Task 定期推送今日订单统计,帮助商家掌握经营情况:

@Component
public class WebSocketTask {
    @Autowired
    private WebSocketServer webSocketServer;
    @Autowired
    private OrderMapper orderMapper;

    @Scheduled(cron = "0/5 * * * * ?")
    public void pushRealTimeData() {
        Map<String, Object> data = new HashMap<>();
        data.put("type", "REAL_TIME_DATA");
        data.put("todayOrders", orderMapper.getTodayOrderCount());
        data.put("pendingOrders", orderMapper.getPendingOrderCount());
        data.put("onlineCount", WebSocketServer.getOnlineCount());

        String jsonMessage = JSON.toJSONString(data);
        webSocketServer.sendToAll(jsonMessage);
    }
}

通过以上组合,我们实现了从订单创建、状态变更到数据统计的全链路实时通知,显著提升了系统的响应速度和用户体验。

目录

  1. Spring Task 与 WebSocket 实战:苍穹外卖订单实时通知方案
  2. Spring Task 入门与实践
  3. 什么是 Spring Task
  4. 快速上手
  5. 1. 开启调度支持
  6. 2. 编写任务类
  7. Cron 表达式详解
  8. 线程池配置(重要)
  9. 最佳实践
  10. WebSocket 实时通信
  11. 服务端实现
  12. 1. 引入依赖
  13. 2. 配置类
  14. 3. 核心业务逻辑
  15. 前端对接
  16. 苍穹外卖场景整合
  17. 无支付场景适配
  18. 来单提醒实现
  19. 催单功能
  20. 定时统计数据推送
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • AI 辅助开发:从需求描述到 SpringBoot 项目生成
  • HarmonyOS 底部导航栏组件 rc_concave_tabbar 使用指南
  • 6 个经典 Python 项目实战指南:爬虫、AI、数据分析、Web、办公与游戏
  • Visual C++ 运行库修复指南:解决 Windows 程序无法启动问题
  • 在浏览器中运行 Python:Pyodide 实战指南
  • GitHub Copilot 学生认证指南:合规申请与常见问题解析
  • Windows 下使用 WSL2 创建 Ubuntu 子系统并配置图形桌面
  • 基于 YOLOv13 的无人机航拍电动自行车违规载人检测系统实战
  • OpenClaw 漏洞预警:如何为 AI 代理添加行为审计?
  • Shannon:基于白盒分析的 AI 自动化渗透测试平台
  • 2026 年 AI 测试用例生成工具 TOP5 深度解析
  • 基于 Rokid 灵珠 AI 平台开发春节全能助手智能体
  • 基于 Web Components 的跨框架组件库实践
  • 5 大 WebAssembly 编译工具性能深度评测
  • Meta-Llama-3-8B-Instruct 在 vLLM 加速下的多轮对话实践
  • 利用 AI 大模型一键生成高清短视频的开源项目介绍
  • llama-cpp-python 完整安装指南与常见问题解决
  • FPGA 部署 YOLOv5 算法的一般流程
  • 基于 Django-Flask 的关爱空巢老人与留守儿童管理系统设计与实现
  • DeepSeek 深度使用指南与高效提示词技巧

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online