@imqueue/core
Version:
Simple JSON-based messaging queue for inter service communication
173 lines • 6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/*!
* UDP message listener for cluster managing: Worker for processing
* messages
*
* I'm Queue Software Project
* Copyright (C) 2025 imqueue.com <support@imqueue.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* If you want to use this code in a closed source (commercial) project, you can
* purchase a proprietary commercial license. Please contact us at
* <support@imqueue.com> to get commercial licensing options.
*/
const worker_threads_1 = require("worker_threads");
const dgram_1 = require("dgram");
const os_1 = require("os");
const uuid_1 = require("./uuid");
process.setMaxListeners(10000);
var MessageType;
(function (MessageType) {
MessageType["Up"] = "up";
MessageType["Down"] = "down";
})(MessageType || (MessageType = {}));
class UDPClusterWorker {
constructor(options, messagePort) {
this.options = options;
this.messagePort = messagePort;
this.servers = new Map();
this.setupMessageHandlers();
this.setupProcessHandlers();
this.socket = (0, dgram_1.createSocket)({
type: 'udp4',
reuseAddr: true,
reusePort: true,
}).bind(this.options.port, this.selectNetworkInterface());
this.socket.on('message', message => this.processMessage(this.parseMessage(message)));
}
static getServerKey(message) {
return message.id;
}
setupMessageHandlers() {
this.messagePort.on('message', message => {
if (message.type === 'stop') {
this.stop();
}
});
}
setupProcessHandlers() {
process.on('SIGTERM', this.cleanup);
process.on('SIGINT', this.cleanup);
process.on('SIGABRT', this.cleanup);
}
addServer(message) {
this.messagePort.postMessage({
type: 'cluster:add',
server: UDPClusterWorker.mapMessage(message),
});
this.serverAliveWait(message);
}
removeServer(message) {
this.servers.delete(UDPClusterWorker.getServerKey(message));
this.messagePort.postMessage({
type: 'cluster:remove',
server: UDPClusterWorker.mapMessage(message),
});
}
static mapMessage(message) {
return {
id: message.id,
name: message.name,
type: message.type,
host: message.host,
port: message.port,
timeout: message.timeout,
};
}
serverAliveWait(message) {
var _a;
const stamp = (0, uuid_1.uuid)();
const correction = (_a = this.options.aliveTimeoutCorrection) !== null && _a !== void 0 ? _a : 0;
const effectiveTimeout = message.timeout + correction + 1;
const key = UDPClusterWorker.getServerKey(message);
this.servers.set(key, stamp);
const t = setTimeout(() => setImmediate(() => {
if (this.servers.get(key) === stamp) {
this.removeServer(message);
}
}), effectiveTimeout);
// Avoid keeping the event loop alive due to pending timers
try {
if (t && typeof t.unref === 'function') {
t.unref();
}
}
catch ( /* ignore */_b) { /* ignore */ }
}
processMessage(message) {
if (message.type === MessageType.Down) {
return this.removeServer(message);
}
if (message.type === MessageType.Up) {
return this.addServer(message);
}
}
selectNetworkInterface() {
const interfaces = (0, os_1.networkInterfaces)();
const broadcastAddress = this.options.address
|| this.options.limitedAddress;
const defaultAddress = '0.0.0.0';
if (!broadcastAddress
|| broadcastAddress === this.options.limitedAddress) {
return defaultAddress;
}
for (const key in interfaces) {
if (!interfaces[key]) {
continue;
}
for (const net of interfaces[key]) {
const shouldBeSelected = net.family === 'IPv4'
&& net.address.startsWith(broadcastAddress.replace(/\.255/g, ''));
if (shouldBeSelected) {
return net.address;
}
}
}
return defaultAddress;
}
parseMessage(input) {
const [name, id, type, address = '', timeout = '0',] = input.toString().split('\t');
const [host, port] = address.split(':');
return {
id,
name,
type: type.toLowerCase(),
host,
port: parseInt(port),
timeout: parseFloat(timeout) * 1000,
};
}
stop() {
this.cleanup();
if (this.socket) {
this.socket.close(() => {
this.messagePort.postMessage({ type: 'stopped' });
});
return;
}
this.messagePort.postMessage({ type: 'stopped' });
}
cleanup() {
this.servers.clear();
if (this.socket) {
this.socket.removeAllListeners();
}
}
}
if (!worker_threads_1.isMainThread && worker_threads_1.parentPort) {
new UDPClusterWorker(worker_threads_1.workerData, worker_threads_1.parentPort);
}
//# sourceMappingURL=UDPWorker.js.map