@tiledesk/tiledesk-server
Version:
The Tiledesk server module
360 lines (274 loc) • 11.6 kB
JavaScript
var amqp = require('amqplib/callback_api');
const winston = require('../../config/winston');
var listeners = [];
class QueueManager {
constructor(url, options) {
this.debug = false;
if (options && options.debug != undefined) {
this.debug = options.debug;
}
this.pubChannel = null;
this.offlinePubQueue = [];
// if the connection is closed or fails to be established at all, we will reconnect
this.amqpConn = null;
this.url = url || "amqp://localhost";
// process.env.CLOUDAMQP_URL + "?heartbeat=60"
// this.exchange = 'amq.topic';
this.exchange = 'tiledeskserver';
if (options && options.exchange != undefined) {
this.exchange = options.exchange;
}
this.defaultTopic = "subscription_run";
if (options && options.defaultTopic != undefined) {
this.defaultTopic = options.defaultTopic;
}
this.topic = "jobsmanager";
if (options && options.topic != undefined) {
this.topic = options.topic;
}
this.deleteTopic = process.env.TRAIN_DELETE_QUEUE || "content_delete";
if (options && options.deleteTopic != undefined) {
this.deleteTopic = options.deleteTopic;
}
this.deleteRoutingKey = process.env.TRAIN_DELETE_ROUTING_KEY || this.deleteTopic;
if (options && options.deleteRoutingKey != undefined) {
this.deleteRoutingKey = options.deleteRoutingKey;
}
if (this.deleteTopic === this.topic) {
throw new Error("QueueManager: deleteTopic (TRAIN_DELETE_QUEUE) must differ from topic (main queue name)");
}
this.prefetch = 1;
if (process.env.TRAIN_PREFETCH) {
this.prefetch = Number(process.env.TRAIN_PREFETCH);
}
// this.listeners = [];
}
connect(callback) {
var that = this;
// console.log("[JobWorker] connect", this.url);
// return new Promise(function (resolve, reject) {
amqp.connect(this.url, function (err, conn) {
if (err) {
// if (this.debug) {console.log("[AMQP]", err.message);
console.error("[JobWorker] AMQP error", err);
return setTimeout(function () {
if (that.debug) { console.log("[JobWorker] AMQP reconnecting"); }
that.connect(callback)
}, 1000);
}
conn.on("error", function (err) {
if (err.message !== "Connection closing") {
console.log("[JobWorker] AMQP conn error", err);
}
});
conn.on("close", function () {
if (that.debug) { console.log("[JobWorker] AMQP reconnecting"); }
return setTimeout(() => {
that.connect(callback);
}, 1000);
});
if (that.debug) { console.log("[JobWorker] AMQP connected"); }
that.amqpConn = conn;
// that.whenConnected(callback);
if (callback) {
callback();
}
// return resolve();
// });
});
}
close(callback) {
if (this.debug) { console.log("Closing connection.."); };
this.amqpConn.close((err) => {
callback(err);
});
}
whenConnected(callback) {
var that = this;
// that.startPublisher(callback);
that.startPublisher();
that.startWorker(callback);
}
startPublisher(callback) {
var that = this;
that.amqpConn.createConfirmChannel(function (err, ch) {
if (that.closeOnErr(err)) return;
ch.on("error", function (err) {
if (that.debug) { console.log("[JobWorker] AMQP channel error", err); }
});
ch.on("close", function () {
if (that.debug) { console.log("[JobWorker] AMQP channel closed"); }
});
// if (this.debug) {console.log("[AMQP] pubChannel");
that.pubChannel = ch;
// console.log("[JobWorker] that.pubChannel",that.pubChannel);
// while (true) {
// var m = that.offlinePubQueue.shift();
// if (!m) break;
// that.publish(m[0], m[1], m[2]);
// }
if (callback) {
callback(err);
}
});
}
// method to publish a message, will queue messages internally if the connection is down and resend later
publish(exchange, routingKey, content, callback) {
var that = this;
if (that.debug) { console.log("[JobWorker] that", that); }
if (that.debug) { console.log("[JobWorker] that.pubChannel", that.pubChannel); }
that.pubChannel.publish(exchange, routingKey, content, { persistent: false },
function (err, ok) {
callback(err, ok)
}
);
// try {
// if (that.debug) {console.log("[JobWorker] that", that);}
// if (that.debug) {console.log("[JobWorker] that.pubChannel", that.pubChannel);}
// that.pubChannel.publish(exchange, routingKey, content, { persistent: false },
// function(err, ok) {
// callback(err, ok)
// // if (err) {
// // // if (that.debug) {console.log("[JobWorker] AMQP publish", err);}
// // // console.log("[JobWorker] AMQP publish", err);
// // // that.offlinePubQueue.push([exchange, routingKey, content]);
// // // that.pubChannel.connection.close();
// // }
// // console.log("publish OK")
// // // if (that.debug) {console.log("[AMQP] publish sent", content);}
// });
// } catch (e) {
// console.log("[JobWorker] AMQP publish catch", e);
// if (this.debug) {console.log("[JobWorker] AMQP publish", e);}
// that.offlinePubQueue.push([exchange, routingKey, content]);
// }
}
// A worker that acks messages only if processed succesfully
// var channel;
startWorker(callback) {
var that = this;
that.amqpConn.createChannel(function (err, ch) {
if (that.closeOnErr(err)) return;
ch.on("error", function (err) {
if (this.debug) { console.log("[JobWorker] AMQP channel error", err); }
});
ch.on("close", function () {
if (this.debug) { console.log("[JobWorker] AMQP channel closed"); }
});
ch.prefetch(that.prefetch);
ch.assertExchange(that.exchange, 'topic', {
//durable: false
durable: true
});
if (that.debug) { console.log("[JobWorker] AMQP that.topic", that.topic); }
// ch.assertQueue(that.topic, { durable: false }, function (err, _ok) {
ch.assertQueue(that.topic, { durable: true }, function (err, _ok) {
if (that.closeOnErr(err)) return;
ch.bindQueue(_ok.queue, that.exchange, that.defaultTopic, {}, function (err3, oka) {
if (that.debug) { console.log("[JobWorker] Queue bind: " + _ok.queue + " err: " + err3 + " key: " + that.defaultTopic); }
// if (this.debug) {console.log("Data queue", oka)
});
ch.bindQueue(_ok.queue, that.exchange, "functions", {}, function (err3, oka) {
if (that.debug) { console.log("[JobWorker] Queue bind: " + _ok.queue + " err: " + err3 + " key: " + "functions"); }
// if (this.debug) {console.log("Data queue", oka)
});
if (that.debug) { console.log("[JobWorker] AMQP that.topic", that.topic); }
ch.consume(_ok.queue, (msg) => {
that.processMsg3(msg, ch);
}, { noAck: false });
ch.assertQueue(that.deleteTopic, { durable: true }, function (errDel, delOk) {
if (that.closeOnErr(errDel)) return;
ch.bindQueue(delOk.queue, that.exchange, that.deleteRoutingKey, {}, function (err4) {
if (that.debug) { console.log("[JobWorker] Delete queue bind: " + delOk.queue + " err: " + err4 + " key: " + that.deleteRoutingKey); }
});
ch.consume(delOk.queue, (msg) => {
that.processDeleteMsg(msg, ch);
}, { noAck: false });
if (that.debug) { console.log("[JobWorker] Delete worker is started on queue " + delOk.queue); }
});
if (that.debug) { console.log("[JobWorker] Worker is started"); }
if (callback) {
callback();
if (that.debug) { console.log("[JobWorker] called callback worker"); }
}
});
});
}
processMsg2(msg) {
// console.log("processMsg2:", msg);
const message_string = msg.content.toString();
// console.log("processMsg2.1:", msg);
const topic = msg.fields.routingKey //.replace(/[.]/g, '/');
// console.log("processMsg2.2:", msg);
// if (this.debug) {console.log("Got msg topic:" + topic);} //this is undefined in this method
// console.log("Got msg topic:" + topic);
// if (this.debug) {console.log("Got msg1:"+ message_string + " topic:" + topic);}
// console.log("Got msg1:"+ message_string + " topic:" + topic);
if (topic === 'functions') {
// if (this.debug) {console.log("Got msg2:"+ JSON.stringify(message_string) + " topic:" + topic);}
// console.log("Got msg2:"+ JSON.stringify(message_string) + " topic:" + topic);
var fdata = JSON.parse(message_string)
// if (this.debug) {console.log("Got msg3:"+ fdata.function + " fdata.function:", fdata.payload);}
/*
// var fields = Object.keys(fdata.payload).map((key) => [key, fdata.payload[key]]);
// var fields = Object.keys(fdata.payload)
// if (this.debug) {console.log("Got fields:"+ fields );
// eval(fdata.function)
*/
if (fdata.function) {
var fn = new Function("payload", fdata.function);
// if (this.debug) {console.log("Got fn:"+ fn);}
/*
// var fn = new Function(fields, fdata.function);
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function
// var fn = new Function("name",'if (this.debug) {console.log("ciao: " + name);');
// fn("andrea")
// var dataArray = Object.keys(fdata.payload).map(function(k){return fdata.payload[k]});
// if (this.debug) {console.log("Got dataArray:", dataArray );
// fn(dataArray);
*/
var ret = fn(fdata.payload)
// if (this.debug) {console.log("Got ret:"+ ret);}
// console.log("Got ret:"+ ret);
}
// else {
// console.log("no function found");
// }
}
// if (topic === 'subscription_run') {
// if (this.debug) {console.log("here topic:" + topic);
// // requestEvent.emit('request.create.queue', msg.content);
// subscriptionEvent.emit('subscription.run.queue', JSON.parse(message_string));
// }
// serve?
// if (this.debug) {console.log("listeners.length:" + listeners.length);}
if (listeners && listeners.length>0) {
for( var i = 0; i< listeners.length; i++) {
// if (this.debug) {console.log("listeners[i]:" + listeners[i]);}
listeners[i](fdata);
}
}
// if (this.debug) {console.log("listeners", this.listeners);
}
closeOnErr(err) {
if (!err) return false;
console.error("[JobWorker] AMQP error", err);
this.amqpConn.close();
return true;
}
sendJson(data, topic, callback) {
this.publish(this.exchange, topic || this.defaultTopic, Buffer.from(JSON.stringify(data)), (err, ok) => {
if (callback) {
callback(err, ok);
}
});
}
send(string, topic) {
if (this.debug) { console.log("[JobWorker] send(string): ", string); }
this.publish(this.exchange, topic || this.defaultTopic, Buffer.from(string));
}
on(fn) {
listeners.push(fn);
}
}
module.exports = QueueManager;