import forever from "forever-monitor"; import { ExecOption, ProcessEvent } from "../typings/exec"; import logFunc from "./log"; export class CommandExec { private _exited: boolean; private _child: forever.Monitor; private _stopPromise: Promise; constructor(public args: string[], public options?: ExecOption) { const logHeader = `args:${JSON.stringify( args )} options:${JSON.stringify(options)}`; let uid = ""; const child = forever.start(args, { silent: true, ...options, fork: true, } as any); this._child = child; logFunc("exec", logHeader); const log = (...largs: any[]) => { logFunc(uid, ...largs); }; child.on("exit", () => { this._exited = true; // max次数执行结束时调用,比如不给max,forever一直在执行,exit不会被触发 // 或者forever的stop执行 log("exit"); }); child.on("start", (o) => { uid = o.uid; // 启动时执行 log("start", logHeader); }); child.on("stop", () => { // forever执行stop,并且exit事件触发后 log("stop"); }); child.on("restart", () => { // 被kill或异常停止或正常停止后会restart log("restart"); }); child.on("watch:restart", (info) => { log("restart on file changed", info.file); }); child.on("stdout", (data) => { // console.log会在stdout log("stdout", data.toString()); }); child.on("stderr", (data) => { // console.error或者抛了异常 log("stderr", data.toString()); }); child.on("error", (err) => { // forever执行产生的错误 log("error", err); }); } /** * 停止命令执行 * 子进程收到TryExit事件后,要在200ms内发送WaitExit, * 如果WaitExit未发送,则过了200ms关闭程序 * 当子进程可以结束时,会发送CanExit */ public async safeStop() { if (this._exited) { return true; } if (this._stopPromise) { return this._stopPromise; } this._stopPromise = new Promise((resolve) => { const child = this._child; const options = this.options; child.send(ProcessEvent.TryExit); // 子进程收到TryExit后,stopMinTimeout后不发WaitExit就退出 const minHandler: any = setTimeout(() => { clearTimeout(maxHandler); child.stop(); }, options.stopMinTimeout || 200); // 子进程发出WaitExit后,stopMaxTimeout后强制停止 const maxHandler: any = setTimeout(() => { child.stop(); }, options.stopMaxTimeout || 60000); // 接收子进程事件清除对应的定时关闭逻辑 const msgFunc = (op) => { if (op === ProcessEvent.WaitExit) { clearTimeout(minHandler); } else if (op === ProcessEvent.CanExit) { child.off("message", msgFunc); clearTimeout(minHandler); clearTimeout(maxHandler); child.stop(); } }; child.on("message", msgFunc); // 关闭完成 child.once("exit", () => { resolve(true); }); }); return this._stopPromise; } // 等待命令结束 public async waitExit() { if (this._exited) { return; } return new Promise((resolve) => { this._child.once("exit", () => { this._exited = true; resolve(true); }); }); } } export default function execExe(args: string[], options?: ExecOption) { return new CommandExec(args, options); }