rmq-wrapper
Version:
285 lines (230 loc) • 10.2 kB
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: index.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: index.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>const logger = require('debug-level')('rmq-wrapper')
const amqp = require('amqp-connection-manager')
const EventEmitter = require('events')
const { uuid } = require('uuidv4')
const RMQ_RPC_TIMEOUT = process.env.RMQ_RPC_TIMEOUT || 10000
const RMQ_CONNECT_TIMEOUT = process.env.RMQ_CONNECT_TIMEOUT || 120 * 1000 // 2 mins
const RMQ_MESSAGE_TTL = process.env.RMQ_MESSAGE_TTL || 10000
const RMQ_HOST = process.env.RMQ_HOST || 'amqp://rabbitmq:5672'
let connection;
/**
* @param {string} [host] default is process.env.RMQ_HOST || 'amqp://rabbitmq:5672'
*/
const connect = async (host = RMQ_HOST) => {
return new Promise((resolve, reject) => {
connection = amqp.connect([host], { json: true })
let timer = setTimeout(() => {
logger.error(`/connect - could not connect to RMQ network within ${RMQ_CONNECT_TIMEOUT / 1000}s`)
reject({ success: false, message: `/connect - could not connect to RMQ network within ${RMQ_CONNECT_TIMEOUT / 1000}s` })
}, RMQ_CONNECT_TIMEOUT)
connection.on('connect', async function () {
logger.info('Connected!')
clearTimeout(timer)
resolve({ success: true, message: "connected" })
})
connection.on('disconnect', function (params) {
logger.error(params)
logger.error('Disconnected.')
})
});
}
/**
* @param {string} name Name of the exchange
* @param {string} [type] 'direct', 'fanout' etc, default is 'direct'
* @param {object} [options] amqp channel.assertExchange options object
*/
Exchange = function (name, type = 'direct', options = { durable: false }) {
if (!name) throw new Error('Missing name parameter')
if (type && typeof type !== 'string') throw "Missing or invalid type parameter, expecting string"
if (options && typeof options !== 'object') throw "Missing or invalid options parameter, expecting object"
this.name = name
this.type = type
this.options = options
}
Exchange.prototype.initializeExchange = async function () {
if (!connection) throw "initializeExchange was called without a valid connection"
let self = this;
try {
self.exchangeChannel = await connection.createChannel({
json: true,
async setup(channel) {
// `channel` here is a regular amqplib `ConfirmChannel`.
// Note that `this` here is the channelWrapper instance.
this.context.channel = channel
return channel.assertExchange(self.name, self.type, self.options)
}
});
self.directChannel = await connection.createChannel({
json: true,
async setup(channel) {
try {
const q = await channel.assertQueue('', { exclusive: true });
this.context.responseEmitter = new EventEmitter()
this.context.responseEmitter.setMaxListeners(0)
this.context.q = q;
this.context.channel = channel
let consume = await channel.consume(
q.queue,
msg => {
self.directChannel.context.responseEmitter.emit(
msg.properties.correlationId,
JSON.parse(msg.content.toString('utf8'))
)
},
{ noAck: true }
)
this.context.directChannelConsumerTag = consume.consumerTag
logger.info("this.context.directChannelConsumerTag")
logger.info(this.context.directChannelConsumerTag)
} catch (e) {
logger.error(e)
}
}
});
return true
} catch (error) {
throw new Error("Failed to initialize exchange")
}
}
/**
* @param {string} queue name of queue
* @param {function} consumeHandler handler function to call when message arrives on queue
* @param {string} [routingKey] optional routing key, defaults to queue name
* @param {number} [prefetch] optional number of messages to prefetch, default is 1
* @param {number} [messageTtl] optional time to live for messages on the queue, default is process.env.RMQ_MESSAGE_TTL || 10000
*/
Exchange.prototype.subscribe = async function (queue, consumeHandler, routingKey = queue, prefetch = 1, messageTtl = RMQ_MESSAGE_TTL ) {
console.log("/subscribe")
if (typeof queue !== 'string') throw "Missing or invalid queue parameter, expecting string"
if (typeof consumeHandler !== 'function') throw "Missing or invalid consumeHandler parameter, expecting function"
if (typeof routingKey !== 'string') throw "Invalid routingKey parameter, expecting string"
if (typeof prefetch !== 'number') throw "Invalid prefetch parameter, expecting number"
if (typeof messageTtl !== 'number') throw "Invalid messageTtl parameter, expecting number"
let self = this
this.exchangeChannel.addSetup(async function (channel) {
try {
await channel.assertQueue(queue, {messageTtl:messageTtl})
await channel.bindQueue(queue, self.name, routingKey)
await channel.prefetch(prefetch)
await channel.consume(queue, consumeHandler)
} catch (e) {
console.log("failed to subscribe")
console.log(e)
throw e
}
});
}
Exchange.prototype.unsubscribe = function (queue, routingKey = queue) {
logger.info("/unsubscribe")
if (!this.exchangeChannel ||
!this.exchangeChannel.context ||
!this.exchangeChannel.context.channel) {
return false
} else {
return this.exchangeChannel.context.channel.unbindQueue(queue, this.name, routingKey)
}
}
/**
* @param {object} message message to ack
*/
Exchange.prototype.ack = function (message) {
if (typeof message === 'undefined') throw "Missing message param"
if (this.exchangeChannel) {
return this.exchangeChannel.ack(message)
}
}
/**
* @param {string} routingKey optional routing key, defaults to queue name
* @param {object} message message to send (JSON object)
* @param {object} [options] amqplib options object for publish method
*/
Exchange.prototype.publish = async function (routingKey, message, options) {
if (typeof routingKey !== 'string') throw "Missing or invalid routingKey parameter, expecting string"
if (typeof message !== 'object') throw "Missing or invalid message parameter, expecting object"
if (options && typeof options !== 'object') throw "Missing or invalid options parameter, expecting object"
return this.exchangeChannel.publish(this.name, routingKey, message, options);
}
/**
* @param {string} queue name of queue
* @param {object} message message to send (JSON object)
* @param {number} [timeout] time to wait for response in ms, default is process.env.RMQ_RPC_TIMEOUT || 10000
*/
Exchange.prototype.sendRPCMessage = async function (queue, message, timeout = RMQ_RPC_TIMEOUT) {
if (typeof queue !== 'string') throw "Missing or invalid queue parameter, expecting string"
if (typeof message !== 'object') throw "Missing or invalid message parameter, expecting object"
if (typeof timeout !== 'number') throw "Missing or invalid timeout parameter, expecting number"
let self = this;
return new Promise((resolve, reject) => {
if (!self.directChannel ||
!self.directChannel.context ||
!self.directChannel.context.responseEmitter) {
reject("Channel not initialized")
} else {
let timer
const q = self.directChannel.context.q;
const correlationId = uuid()
self.directChannel.context.responseEmitter.once(correlationId, (response) => {
clearTimeout(timer)
resolve(response)
})
timer = setTimeout(() => {
logger.error("/sendRPCMessage - response not received within 10s")
reject("Message not received within required time")
}, timeout)
self.directChannel.sendToQueue(queue, message, {
correlationId,
replyTo: q.queue
})
}
})
}
/**
* @param {object} message the original message to reply to
* @param {object} reply JSON content of the reply messsage
*/
Exchange.prototype.replyToRPC = async function (message, reply) {
if (typeof message !== 'object') throw "Missing or invalid message parameter, expecting object"
if (typeof reply !== 'object') throw "Missing or invalid reply parameter, expecting object"
let self = this;
self.exchangeChannel.sendToQueue(message.properties.replyTo, reply, {
correlationId: message.properties.correlationId
});
return
}
module.exports = {
Exchange,
connect
}
</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#connect">connect</a></li><li><a href="global.html#Exchange">Exchange</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.2</a> on Wed Feb 26 2020 12:31:33 GMT+0000 (Greenwich Mean Time)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>