/** * cli command processing module such as omelot list */ import Application from '../application'; import define = require('../util/define'); import { Master_ServerProxy, Master_ClientProxy } from './master'; import { MonitorClientProxy } from './monitor'; const serverTypeSort: string[] = []; interface requset { cb: Function; timeOut: NodeJS.Timeout; } export class MasterCli { private readonly app: Application; private readonly servers: Map; private monitorRequests: Record = {}; private reqId: number = 1; private exiting = false; // 进程是否正在退出 constructor(app: Application, servers: Map) { this.app = app; this.servers = servers; serverTypeSort.push('master'); for (const svrType in app.serversConfig) { serverTypeSort.push(svrType); } } deal_cli_msg(socket: Master_ClientProxy, data: any) { const reqId = data.reqId; data = data.msg; // eslint-disable-next-line @typescript-eslint/restrict-plus-operands if ((this as any)['func_' + data.func]) { // eslint-disable-next-line @typescript-eslint/restrict-plus-operands (this as any)['func_' + data.func](reqId, socket, data.args); } } deal_monitor_msg(data: { reqId: number; msg: any }) { const req = this.monitorRequests[data.reqId]; if (req) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete this.monitorRequests[data.reqId]; clearTimeout(req.timeOut); req.cb(null, data.msg); } } private send_to_monitor(socket: Master_ServerProxy | undefined, msg: { 'func': string; 'args'?: any }, timeout: number, cb?: Function) { const data = { T: define.Master_To_Monitor.cliMsg, msg } as any; if (cb) { const _reqId = this.reqId++; data.reqId = _reqId; const self = this; this.monitorRequests[_reqId] = { cb, timeOut: setTimeout(function () { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete self.monitorRequests[_reqId]; // eslint-disable-next-line n/no-callback-literal cb('time out'); }, timeout * 1000) }; } socket?.send(data); } private async func_list(reqId: number, socket: Master_ClientProxy, _args: any) { const self = this; let num = 0; for (const ms of this.servers.values()) { num++; this.send_to_monitor(ms, { func: 'list' }, 10, cb); } const titles = ['id', 'serverType', 'pid', 'rss(M)', 'upTime(d-h-m)']; const infos = getListInfo(this.app); const ListFunc: any = this.app.someconfig.omelotList; if (typeof ListFunc === 'function') { const resArr = await ListFunc(); if (resArr && Array.isArray(resArr)) { for (const one of resArr) { titles.push(one.title); infos.push(one.value); } } } const serverInfoArr: string[][] = []; serverInfoArr.push(titles); serverInfoArr.push(infos); if (num === 0) { // eslint-disable-next-line n/no-callback-literal cb('no other server', null); } function cb(err: any, data: any) { if (!err) { serverInfoArr.push(data); } num--; if (num <= 0) { socket.send({ reqId, msg: { name: self.app.appName, env: self.app.env, serverTypeSort, infoArr: serverInfoArr } }); } } } private func_stop(reqId: number, socket: Master_ClientProxy, _args: string[]) { let num = this.servers.size; if (num === 0) { // eslint-disable-next-line n/no-callback-literal cb('no server', null); return; } if (this.exiting) { socket.send({ reqId }); return; } this.exiting = true; for (const ms of this.servers.values()) { this.send_to_monitor(ms, { func: 'stop' }, 3600, cb); } function cb(_err: any, _data: any) { num--; if (num <= 0) { socket.send({ reqId }); exitCall(); } } } private func_remove(reqId: number, socket: Master_ClientProxy, args: string[]) { args = Array.from(new Set(args)); let num = 0; for (let i = 0; i < args.length; i++) { if (!this.servers.has(args[i])) { continue; } num++; this.send_to_monitor(this.servers.get(args[i]), { func: 'remove' }, 3600, cb); } if (num === 0) { // eslint-disable-next-line n/no-callback-literal cb('no server', null); } function cb(_err: any, _data: any) { num--; if (num <= 0) { socket.send({ reqId }); } } } private func_removeT(reqId: number, socket: Master_ClientProxy, args: string[]) { args = Array.from(new Set(args)); let num = 0; for (const one of this.servers.values()) { if (!args.includes(one.serverType)) { continue; } num++; this.send_to_monitor(one, { func: 'removeT' }, 3600, cb); } if (num === 0) { // eslint-disable-next-line n/no-callback-literal cb('no serverType', null); } function cb(_err: any, _data: any) { num--; if (num <= 0) { socket.send({ reqId }); } } } private func_send(reqId: number, socket: Master_ClientProxy, args: { 'serverIds': string[]; 'serverTypes': string[]; 'argv': string[] }) { const okArrSet = new Set(); if (args.serverIds.length) { for (const id of args.serverIds) { if (this.servers.has(id)) { okArrSet.add(this.servers.get(id)); } } } if (args.serverTypes.length) { for (const one of this.servers.values()) { if (args.serverTypes.includes(one.serverType)) { okArrSet.add(one); } } } if (args.serverIds.length === 0 && args.serverTypes.length === 0) { for (const x of this.servers.values()) { okArrSet.add(x); } } const okArr = Array.from(okArrSet); if (okArr.length === 0) { socket.send({ reqId, msg: { err: 'no target serverIds' } }); return; } let num = okArr.length; const endData: Array<{ id: string | undefined; serverType: string | undefined; data: any }> = []; const timeoutIds: Array = []; for (const one of okArr) { this.send_to_monitor(one, { func: 'send', args: args.argv }, 600, (err: any, data: any) => { if (err) { timeoutIds.push(one?.sid); } else { endData.push({ id: one?.sid, serverType: one?.serverType, data }); } num--; if (num <= 0) { socket.send({ reqId, msg: { err: '', timeoutIds, data: endData } }); } }); } } } function getListInfo(app: Application) { const mem = process.memoryUsage(); const Mb = 1024 * 1024; return [app.serverId, app.serverType, process.pid.toString(), Math.floor(mem.rss / Mb).toString(), formatTime(app.startTime)]; } function formatTime(time: number) { time = Math.floor((Date.now() - time) / 1000); const days = Math.floor(time / (24 * 3600)); time = time % (24 * 3600); const hours = Math.floor(time / 3600); time = time % 3600; const minutes = Math.ceil(time / 60); return `${days}-${hours}-${minutes}`; } export class MonitorCli { private readonly app: Application; private exiting = false; // 进程是否正在退出 constructor(app: Application) { this.app = app; } deal_master_msg(socket: MonitorClientProxy, data: any) { const reqId = data.reqId; data = data.msg; // eslint-disable-next-line @typescript-eslint/restrict-plus-operands if ((this as any)['func_' + data.func]) { // eslint-disable-next-line @typescript-eslint/restrict-plus-operands (this as any)['func_' + data.func](reqId, socket, data.args); } } private send_to_master(socket: MonitorClientProxy, msg: any) { socket.send(msg); } private async func_list(reqId: number, socket: MonitorClientProxy, _args: any) { const infos = getListInfo(this.app); const ListFunc: any = this.app.someconfig.omelotList; if (typeof ListFunc === 'function') { const resArr = await ListFunc(); if (resArr && Array.isArray(resArr)) { for (const one of resArr) { infos.push(one.value); } } } const msg = { T: define.Monitor_To_Master.cliMsg, reqId, msg: infos }; this.send_to_master(socket, msg); } private func_stop(reqId: number, socket: MonitorClientProxy, _args: any) { const msg = { T: define.Monitor_To_Master.cliMsg, reqId }; if (this.exiting) { return; } this.exiting = true; const exitFunc = this.app.someconfig.onBeforeExit; if (exitFunc) { exitFunc(() => { this.send_to_master(socket, msg); exitCall(); }); } else { this.send_to_master(socket, msg); exitCall(); } } private func_remove(reqId: number, socket: MonitorClientProxy, _args: any) { const msg = { T: define.Monitor_To_Master.cliMsg, reqId }; if (this.exiting) { return; } this.exiting = true; const exitFunc = this.app.someconfig.onBeforeExit; if (exitFunc) { exitFunc(() => { this.send_to_master(socket, msg); exitCall(); }); } else { this.send_to_master(socket, msg); exitCall(); } } private func_removeT(reqId: number, socket: MonitorClientProxy, _args: any) { const msg = { T: define.Monitor_To_Master.cliMsg, reqId }; if (this.exiting) { return; } this.exiting = true; const exitFunc = this.app.someconfig.onBeforeExit; if (exitFunc) { exitFunc(() => { this.send_to_master(socket, msg); exitCall(); }); } else { this.send_to_master(socket, msg); exitCall(); } } private func_send(reqId: number, socket: MonitorClientProxy, args: string[]) { const msg = { T: define.Monitor_To_Master.cliMsg, reqId, msg: null }; const sendFunc = this.app.someconfig.onOmelotSend; if (sendFunc) { sendFunc(args, (data) => { if (data === undefined) { data = null; } msg.msg = data; this.send_to_master(socket, msg); }); } else { this.send_to_master(socket, msg); } } } /** 进程 1s 后退出 */ function exitCall() { setTimeout(() => { process.exit(); }, 1000); }