'use strict'; const cluster = require('cluster'); const sendmessage = require('sendmessage'); const debug = require('debug')('egg-cluster:messenger'); /** * master messenger,provide communication between parent, master, agent and app. * * ┌────────┐ * │ parent │ * /└────────┘\ * / | \ * / ┌────────┐ \ * / │ master │ \ * / └────────┘ \ * / / \ \ * ┌───────┐ ┌───────┐ * │ agent │ ------- │ app │ * └───────┘ └───────┘ * * * in app worker * * ```js * process.send({ * action: 'xxx', * data: '', * to: 'agent/master/parent', // default to app * }); * ``` * * in agent worker * * ```js * process.send({ * action: 'xxx', * data: '', * to: 'app/master/parent', // default to agent * }); * ``` * * in parent * * ```js * process.send({ * action: 'xxx', * data: '', * to: 'app/agent/master', // default to be ignore * }); * ``` */ class Messenger { constructor(master) { this.master = master; this.hasParent = !!process.send; process.on('message', msg => { msg.from = 'parent'; this.send(msg); }); process.once('disconnect', () => { this.hasParent = false; }); } /** * send message * @param {Object} data message body * - {String} from from who * - {String} to to who */ send(data) { if (!data.from) { data.from = 'master'; } // recognise receiverPid is to who if (data.receiverPid) { if (data.receiverPid === String(process.pid)) { data.to = 'master'; } else if (data.receiverPid === String(this.master.agentWorker.pid)) { data.to = 'agent'; } else { data.to = 'app'; } } // default from -> to rules if (!data.to) { if (data.from === 'agent') data.to = 'app'; if (data.from === 'app') data.to = 'agent'; if (data.from === 'parent') data.to = 'master'; } // app -> master // agent -> master if (data.to === 'master') { debug('%s -> master, data: %j', data.from, data); // app/agent to master this.sendToMaster(data); return; } // master -> parent // app -> parent // agent -> parent if (data.to === 'parent') { debug('%s -> parent, data: %j', data.from, data); this.sendToParent(data); return; } // parent -> master -> app // agent -> master -> app if (data.to === 'app') { debug('%s -> %s, data: %j', data.from, data.to, data); this.sendToAppWorker(data); return; } // parent -> master -> agent // app -> master -> agent,可能不指定 to if (data.to === 'agent') { debug('%s -> %s, data: %j', data.from, data.to, data); this.sendToAgentWorker(data); return; } } /** * send message to master self * @param {Object} data message body */ sendToMaster(data) { this.master.emit(data.action, data.data); } /** * send message to parent process * @param {Object} data message body */ sendToParent(data) { if (!this.hasParent) { return; } process.send(data); } /** * send message to app worker * @param {Object} data message body */ sendToAppWorker(data) { for (const id in cluster.workers) { const worker = cluster.workers[id]; if (worker.state === 'disconnected') { continue; } // check receiverPid if (data.receiverPid && data.receiverPid !== String(worker.process.pid)) { continue; } sendmessage(worker, data); } } /** * send message to agent worker * @param {Object} data message body */ sendToAgentWorker(data) { if (this.master.agentWorker) { sendmessage(this.master.agentWorker, data); } } } module.exports = Messenger;