UNPKG

node-cluster-ipc

Version:

A lightweight for Inter-Process Communication (IPC) between master and worker processes using the cluster module

130 lines (129 loc) 4.66 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ClusterIpc = void 0; const cluster_1 = __importDefault(require("cluster")); const crypto_1 = require("crypto"); const events_1 = require("events"); class ClusterIpc extends events_1.EventEmitter { constructor(options = { requestTimeout: 5000 }) { super(); this.options = options; this.workerIndex = 0; this.workerMap = new Map(); this.pendingRequests = new Map(); this.isPrimary ? this.setupPrimary() : this.setupWorker(); } get isPrimary() { return cluster_1.default.isPrimary || cluster_1.default.isMaster; } get isWorker() { return cluster_1.default.isWorker; } get worker() { return cluster_1.default.worker; } get workers() { return cluster_1.default.workers; } send(channel, data, workerId) { const message = { channel, data }; const worker = this.isPrimary ? this.getWorker(workerId) : this.worker; if (worker) worker.send(message); } publish(channel, data) { if (!this.isPrimary) { throw new Error('Method "publish" can only be called from the primary process'); } const message = { channel, data }; const workers = Object.values(this.workers || {}); if (workers.length) { workers.forEach(worker => worker.send(message)); } else { throw new Error('No workers available'); } } async request(channel, data, workerId) { const requestId = (0, crypto_1.randomUUID)(); const message = { channel, data, requestId }; return new Promise((resolve, reject) => { const timeout = setTimeout(() => { const request = this.pendingRequests.get(requestId); if (request) { this.pendingRequests.delete(requestId); reject(new Error('Request timeout')); } }, this.options.requestTimeout); this.pendingRequests.set(requestId, { resolve, reject, timeout }); try { const worker = this.isPrimary ? this.getWorker(workerId) : this.worker; if (worker) worker.send(message); } catch (error) { clearTimeout(timeout); this.pendingRequests.delete(requestId); reject(error); } }); } reply(channel, data, requestId, workerId) { const message = { channel, data, requestId, isReply: true }; const worker = this.isPrimary ? this.getWorker(workerId) : this.worker; if (worker) worker.send(message); } setupPrimary() { cluster_1.default.on('online', (worker) => { worker.on('message', (msg) => this.handleMessage(msg, worker)); }); } setupWorker() { const worker = this.worker; worker.on('message', (msg) => this.handleMessage(msg, worker)); } handleMessage(msg, worker) { if (msg.isReply && msg.requestId) { const request = this.pendingRequests.get(msg.requestId); if (request) { clearTimeout(request.timeout); this.pendingRequests.delete(msg.requestId); request.resolve(msg.data); } } else if (msg.requestId) { this.emit('request', msg.channel, msg.data, (response) => { this.reply(msg.channel, response, msg.requestId, worker.id); }); } else { this.emit('message', msg.channel, msg.data); } } getWorker(id) { const workers = Object.values(this.workers || {}); if (workers.length === 0) { throw new Error('No workers available'); } if (id !== undefined) { const worker = this.workers[id]; if (worker) return worker; const workerId = this.workerMap.get(String(id)); if (workerId !== undefined) { const worker = this.workers[workerId]; if (worker) return worker; } } const worker = workers[this.workerIndex]; this.workerIndex = (this.workerIndex + 1) % workers.length; this.workerMap.set(String(id), worker.id); return worker; } } exports.ClusterIpc = ClusterIpc;