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

Node.js Promise循环中停止Windows服务控制器重复查询问题

解决ColdFusion 10服务启动重试时sc命令重复输出的问题

我来帮你搞定这个问题,同时优化下你的重试逻辑。首先,重复输出的根源是sc模块默认会把所有命令执行过程和结果打印到控制台,每次重试都会触发这些输出;其次,递归Promise的重试方式虽然能跑,但用async/await循环会更清晰可靠。

问题重现

你提到的重复输出示例:

sc \\abnf34873 start ColdFusion 10 Application Server
sc \\abnf34873 queryex ColdFusion 10 Application Server
SERVICE_NAME: ColdFusion 10 Application Server
TYPE               : 10  WIN32_OWN_PROCESS
STATE              : 2  START_PENDING (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE    : 0  (0x0)
SERVICE_EXIT_CODE  : 0  (0x0)
CHECKPOINT         : 0x0
WAIT_HINT          : 0x7d0
PID                : 0
FLAGS              :
SERVICE_NAME: ColdFusion 10 Application Server
TYPE               : 10  WIN32_OWN_PROCESS
STATE              : 2  START_PENDING (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE    : 0  (0x0)
SERVICE_EXIT_CODE  : 0  (0x0)
CHECKPOINT         : 0x0
WAIT_HINT          : 0x7d0
PID                : 13772
FLAGS              :

解决方案

1. 关闭sc模块的默认日志输出

大多数Windows服务管理的Node.js模块(比如sc-windows)都会提供quiet或silent选项来禁用自动控制台输出。我们只需要在调用sc.query和sc.start时添加这个参数,就能避免重复打印命令和服务状态。

2. 优化重试逻辑

把递归Promise改成async/await循环,不仅代码可读性更高,还能避免递归栈溢出的风险,同时错误处理也更清晰。

完整优化代码

const gulp = require('gulp');
const sc = require('sc-windows'); // 假设你使用的是sc-windows模块
const kill = require('tree-kill'); // 假设kill依赖是tree-kill

// 配置常量,方便后续维护
const MAX_RETRIES = 3;
const SERVICE_NAME = 'ColdFusion 10 Application Server';

gulp.task('start-coldfusion-service', async function(done) {
  console.log("Starting ColdFusion service..");
  
  // 全局配置sc的轮询间隔和超时,只需设置一次
  sc.pollInterval(30);
  sc.timeout(60);

  let retryCount = 0;
  let serviceStarted = false;

  while (retryCount < MAX_RETRIES && !serviceStarted) {
    try {
      serviceStarted = await tryServiceStart();
      if (!serviceStarted) {
        retryCount++;
        console.log(`Retry Count: ${retryCount}`);
      }
    } catch (err) {
      retryCount++;
      console.log(`Problem starting ColdFusion service! Error: ${err.message}`);
      console.log(`Retrying... (${retryCount}/${MAX_RETRIES})`);
    }
  }

  if (serviceStarted) {
    console.log('✅ ColdFusion service started successfully!');
    done();
  } else {
    done(new Error(`❌ Failed to start ColdFusion service after ${MAX_RETRIES} retries`));
  }
});

async function tryServiceStart() {
  // 查询服务状态,禁用默认日志输出
  const services = await sc.query(targetServer, { name: SERVICE_NAME, quiet: true });

  if (services.length !== 1) {
    console.log("Could not find the ColdFusion service.");
    return false;
  }

  const service = services[0];

  // 处理启动挂起的情况:杀死进程后返回false触发重试
  if (service.state.name === 'START_PENDING') {
    console.log('Service is stuck in START_PENDING, killing hanging process..');
    try {
      await kill(service.pid, { force: true });
      console.log('Hanging process killed successfully.');
      return false;
    } catch (err) {
      console.log(`Failed to kill process: ${err.message}`);
      return false;
    }
  }

  // 如果服务已经在运行,直接返回成功
  if (service.state.name === 'RUNNING') {
    console.log('ColdFusion service is already running.');
    return true;
  }

  // 服务已停止,尝试启动
  if (service.state.name === 'STOPPED') {
    try {
      await sc.start(targetServer, SERVICE_NAME, { quiet: true });
      // 额外查询一次状态,确保服务确实进入运行状态
      const updatedService = await sc.query(targetServer, { name: SERVICE_NAME, quiet: true });
      const isRunning = updatedService[0].state.name === 'RUNNING';
      if (isRunning) {
        console.log('Service start initiated successfully.');
      } else {
        console.log('Service start initiated but not yet running.');
      }
      return isRunning;
    } catch (err) {
      console.log(`Service start failed: ${err.message}`);
      throw err; // 抛出错误触发重试
    }
  }

  // 其他状态(比如STOP_PENDING等),暂时返回false
  console.log(`Service is in ${service.state.name} state, cannot start now.`);
  return false;
}

关键优化点说明

  • 禁止重复输出:通过{ quiet: true }参数关闭sc模块的自动日志,所有日志都由我们手动控制,只打印必要信息。
  • async/await循环重试:替代递归Promise,逻辑更直观,避免递归栈溢出,同时错误处理更可控。
  • 严谨的状态检查:启动服务后额外查询状态,确保服务真正进入RUNNING状态,而不是仅依赖start方法的成功回调。
  • 可维护性:把常量(重试次数、服务名)抽离出来,方便后续修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:35:39