UNPKG

grace-server

Version:

通过cluster集群,加强node服务器的稳定性

158 lines (152 loc) 5.36 kB
const {delay, typeCheck} = require('../util/util'); const {log, TAGS} = require('../util/log'); const {reloadDelay} = require('./readConfig'); function TaskMannger(cluster) { // 监听task列表,由此可以触发子进程热更,每一个task对应一个子进程热更函数 this.taskMap = {}; // 任务锁 this.taskLock = false; // cluster this.cluster = cluster; } // 参数说明: // 柯里化接受参数 // 需要传入worker任务hash列表task实例 和执行回调 // 函数说明: // 此函数在当worker断开链接时执行 // 首先运行addWorker函数fork一个新的worker,执行server流程 // 之后执行callback let addWorker = async task => { // 此时,子进程已经平滑断开,进行重启子进程 try { await task.addWorker(); // 子进程热更函数callBack log('worker fork success!', TAGS.SUCCESS); await delay(+reloadDelay < 50 ? +reloadDelay : 50); return true; } catch (e) { // log('error in addworker function.', TAGS.FAILED); // console.log(e); return false; } }; // 将子进程热更函数添加到taskMap,等待触发 // 参数说明: // 需要接受task对象 和 具体的worker,是一个工厂函数 // 此工厂函数返回一个可以close worker的执行函数 // 用来完成给具体的worker发送关闭信息 let taskWorkerFunc = (task, worker) => () => new Promise(resolve => { // 执行task热更,此时删除当前task delete task.taskMap[worker.id]; if (worker.connected) { // 向子进程发送关闭信号 worker.send('close'); // 子程序处理完关闭,断开连接时 worker.on('disconnect', async () => { await addWorker(task); resolve(); }); } else { worker.kill('SIGKILL'); addWorker(task).then(resolve); } }); // 保证worker fork初始化完成 // 需要为worker 添加回调 // 使用函数柯里化接受参数 let onWorkerForkSuccess = (successCallBack, failCallback) => async msg => { if (msg === 'forkSuccess') { successCallBack(); return; } await delay(5000); failCallback(); }; // addWorker函数 // 此函数将fork出一个新的worker // 当fork完成后worker将运行server流程 // server流程运行完毕,worker将发送forker success message // 此函数进行监听信息,收到fork success 之后,将此worker 添加如taskMap // 之后如果重启,则执行 graceReload 函数 即可运行taskMap里面的函数对worker进行关闭 const restartWorker = ((ptime = 0, maxTimeStep = 5e2, pendingCount = 0, timeStart = new Date().getTime()) => async (task, worker) => { let ctime = new Date().getTime(); if (ptime === 0) { ptime = ctime; } // TODO 将spinning 的最小间隔 抽象到config // timeStep为当前时刻与上一时刻的间距, let timeStep = ctime - ptime; let spinning = timeStep < maxTimeStep; task.taskMap = task.taskMap || {}; let taskMap = task.taskMap; if (!taskMap[worker.id]) { return; } log('时间:' + (new Date().getTime() - timeStart), TAGS.FAILED); if (spinning) { pendingCount++; // 防止一次性触发的重启很多,防止将很多重启延时到下一个相同的时间 // 也就是说,需要将 在同一时刻触发的重启 分到不同的下一时刻。 let spinSleepTime = pendingCount * maxTimeStep + ptime - ctime; await delay(spinSleepTime); pendingCount--; } ptime = new Date().getTime(); taskMap[worker.id] && await taskMap[worker.id](); })() TaskMannger.prototype.addWorker = function () { if (!this.cluster) { log('there is no cluster', TAGS.FAILED); return; } let worker = this.cluster.fork(); // 子进程意外退出 worker.on('exit', async code => { log('worker process exit in code:' + code, code ? TAGS.FAILED : TAGS.SUCCESS); await restartWorker(this, worker); }) // 返回一个promise return new Promise((resolve, reject) => { // fork成功,且运行成功 worker.on('message', onWorkerForkSuccess(resolve, reject)); // fork成功,但是worker退出 worker.on('exit', onWorkerForkSuccess(resolve, reject)); this.taskMap[worker.id] = taskWorkerFunc(this, worker); }); }; TaskMannger.prototype.graceReload = async function () { console.log(this.taskLock); if (this.taskLock) { return; } this.taskLock = true; log('start graceReload', TAGS.INFO); // 处理task try { for (let key in this.taskMap) { if (!this.taskMap.hasOwnProperty(key)) { continue; } // 处理this.taskMap if (!typeCheck(this.taskMap[key], 'Function')) { throw 'failed graceReload'; } await this.taskMap[key](); await delay(reloadDelay); } log('success graceReload' + Object.keys(this.taskMap).join(','), TAGS.SUCCESS); this.taskLock = false; } catch (e) { if (typeCheck(e, 'String')) { log(e, TAGS.FAILED); } console.log(e); this.taskLock = false; } }; module.exports = { TaskMannger };