const path = require('path'); const EventEmitter = require('events'); const { fork } = require('child_process'); const serialize = require('serialize-javascript'); const Log = require('../../log'); const Ps = require('../../ps'); const Channel = require('../../const/channel'); const Helper = require('../../utils/helper'); class ForkProcess { constructor(host, opt = {}) { let cwd = Ps.getHomeDir(); let appPath = path.join(__dirname, 'app.js'); if (Ps.isPackaged()) { // todo fork的cwd目录为什么要在app.asar外 ? cwd = path.join(Ps.getHomeDir(), '..'); } // TODO Object.assign 只能单层对象结构,多层的对象会直接覆盖 let options = Object.assign({ processArgs: {}, processOptions: { cwd: cwd, env: Ps.allEnv(), stdio: 'ignore' // pipe } }, opt); this.emitter = new EventEmitter(); this.host = host; this.args = []; this.sleeping = false; // 传递给子进程的参数 this.args.push(JSON.stringify(options.processArgs)); this.child = fork(appPath, this.args, options.processOptions); this.pid = this.child.pid; this._init(); } /** * 初始化事件监听 */ _init() { const { messageLog } = this.host.config; this.child.on('message', (m) => { if (messageLog == true) { Log.coreLogger.info(`[ee-core] [jobs/child] received a message from child-process, message: ${serialize(m)}`); } if (m.channel == Channel.process.showException) { Log.coreLogger.error(`${m.data}`); } // 收到子进程消息,转发到 event if (m.channel == Channel.process.sendToMain) { this._eventEmit(m); } }); this.child.on('exit', (code, signal) => { let data = { pid: this.pid } this.host.emit(Channel.events.childProcessExit, data); Log.coreLogger.info(`[ee-core] [jobs/child] received a exit from child-process, code:${code}, signal:${signal}, pid:${this.pid}`); }); this.child.on('error', (err) => { let data = { pid: this.pid } this.host.emit(Channel.events.childProcessError, data); Log.coreLogger.error(`[ee-core] [jobs/child] received a error from child-process, error: ${err}, pid:${this.pid}`); }); } /** * event emit */ _eventEmit(m) { switch (m.eventReceiver) { case Channel.receiver.forkProcess: this.emitter.emit(m.event, m.data); break; case Channel.receiver.childJob: this.host.emit(m.event, m.data); break; default: this.host.emit(m.event, m.data); this.emitter.emit(m.event, m.data); break; } } /** * 分发任务 */ dispatch(cmd, jobPath = '', params = {}) { // 消息对象 const mid = Helper.getRandomString(); let msg = { mid, cmd, jobPath, jobParams: params } // todo 是否会发生监听未完成时,接收不到消息? // 发消息到子进程 this.child.send(msg); } /** * kill */ kill(timeout = 1000) { this.child.kill('SIGINT'); setTimeout(() => { if (this.child.killed) return; this.child.kill('SIGKILL'); }, timeout) } /** * sleep (仅Unix平台) */ sleep() { if (this.sleeping) return; process.kill(this.pid, 'SIGSTOP'); this.sleeping = true; } /** * wakeup (仅Unix平台) */ wakeup() { if (!this.sleeping) return; process.kill(this.pid, 'SIGCONT'); this.sleeping = false; } } module.exports = ForkProcess;