You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何重构Node.js代码,使应用在每日任务结束后等待下一次Cron调度?

Node.js股票数据应用重构方案

核心问题

你的问题本质是WebSocket服务生命周期和Cron任务未解耦,导致清理操作后旧连接无法正常终止,状态重置逻辑依赖main()递归引发实例堆积,最终客户端无限重连直到崩溃。

重构步骤

1. 拆分模块,解耦核心逻辑

把WebSocket服务、数据收集、状态管理拆成独立可控的单元,避免所有逻辑堆在main()里导致状态耦合:

// 模块级全局状态(替代局部变量,方便跨任务访问)
let wsServer = null;
let isFirstCall = true;
let stockDataList = [];

// 启动WebSocket服务
function startWebSocketService() {
  const WebSocket = require('ws');
  wsServer = new WebSocket.Server({ port: 你使用的端口 });

  wsServer.on('connection', (ws) => {
    // 推送现有股票数据
    if (stockDataList.length) {
      ws.send(JSON.stringify(stockDataList));
    }

    // 捕获连接错误,避免单个客户端崩溃拖垮整个服务
    ws.on('error', (err) => {
      console.error('客户端连接异常:', err);
    });
  });
}

// 关闭WebSocket服务(关键:主动终止所有连接)
function stopWebSocketService() {
  if (!wsServer) return;

  // 先给所有客户端发送明确关闭信号,告知无需立即重连
  wsServer.clients.forEach(client => {
    if (client.readyState === client.OPEN) {
      client.close(1000, '每日数据清理完成,次日6点后可重新连接');
    }
  });

  // 关闭服务端实例
  wsServer.close(err => {
    if (err) console.error('WebSocket服务关闭失败:', err);
    wsServer = null; // 重置实例引用
  });
}

// 股票数据收集逻辑
async function fetchStockData() {
  // 替换成你的API查询逻辑
  stockDataList = await 你的API查询函数();
  isFirstCall = false;
}

// 每日20:05清理任务
const dailyCleanup = require('node-cron').schedule('0 5 20 * * *', () => {
  stopWebSocketService(); // 先关WS
  stockDataList = []; // 清空数据
  isFirstCall = true; // 重置状态
  console.log('每日清理完成,等待次日调度');
});

// 每日6:00启动任务
const dailyStart = require('node-cron').schedule('0 0 6 * * *', async () => {
  await fetchStockData(); // 收集数据
  startWebSocketService(); // 启动WS
  console.log('次日任务启动,WebSocket服务已就绪');
});

// 应用初始化(首次启动执行)
async function initApp() {
  await fetchStockData();
  startWebSocketService();
  console.log('应用初始化完成');
}

initApp();

2. 关键修复说明

  • WebSocket实例全局可控:通过模块级变量wsServer维护服务实例,清理任务可以直接调用stopWebSocketService()主动关闭所有连接,从根源解决客户端无限重连的问题
  • 状态变量独立管理:isFirstCall和stockDataList改成模块级,清理时直接重置,不需要依赖main()递归调用
  • Cron任务职责分离:启动任务负责数据收集+WS启动,清理任务负责WS关闭+状态重置,互不干扰,避免实例堆积
  • 错误防护:给WebSocket连接添加错误捕获,避免单个客户端的异常导致整个应用崩溃

3. 为什么之前调用main()失败?

直接递归调用main()会重复创建Cron任务、WebSocket服务实例,旧的服务实例不会被销毁,客户端会一直连接旧实例,同时新实例的状态和旧实例冲突,最终导致内存泄漏和重连异常。

4. 额外优化

  • 给所有关键操作添加日志,方便排查问题
  • 可以给客户端的重连逻辑添加延迟,比如收到关闭信号后,等到次日6点再尝试重连
  • 给Cron任务添加错误捕获,避免任务执行失败导致应用崩溃

内容的提问来源于stack exchange,提问作者ADSquared

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 12:10:18