|
1 | 1 | import * as winston from "winston"; |
2 | 2 | import * as amqplib from "amqplib"; |
3 | 3 | import { NoderedUtil } from "./nodered/nodes/NoderedUtil"; |
| 4 | +import { Config } from './Config'; |
4 | 5 |
|
5 | 6 |
|
6 | 7 | interface IHashTable<T> { |
@@ -65,10 +66,42 @@ export class amqp_publisher { |
65 | 66 | if (this.channel != null && this.channel != undefined) { await this.channel.close(); this.channel = null; } |
66 | 67 | if (this.conn != null && this.conn != undefined) { await this.conn.close(); this.conn = null; } |
67 | 68 | } |
68 | | - SendMessage(msg: string, queue: string, correlationId: string, sendreply: boolean): void { |
| 69 | + async SendMessage(msg: string, queue: string, correlationId: string, sendreply: boolean): Promise<void> { |
69 | 70 | if (correlationId == null || correlationId == "") { correlationId = this.generateUuid(); } |
70 | 71 | this._logger.info("SendMessage " + msg); |
| 72 | + |
71 | 73 | if (sendreply) { |
| 74 | + // Before sending the message, need to assert the exchange and queue to handle timed out messages |
| 75 | + // This is done via a dead letter exchange, and dead letter queue |
| 76 | + const dlx = await this.channel.assertExchange(Config.amqp_dlx_prefix + queue, 'topic', { durable: false }); |
| 77 | + const dlq = await this.channel.assertQueue(Config.amqp_dlq_prefix + queue, { durable: false }); |
| 78 | + // Bind the dead letter queue to the dead letter exchange, routing with the dead letter routing key |
| 79 | + await this.channel.bindQueue(dlq.queue, dlx.exchange, Config.amqp_dlrk_prefix + queue); |
| 80 | + |
| 81 | + // Must also consume messages in the dead letter queue, to catch messages that have timed out |
| 82 | + await this.channel.consume(dlq.queue, msg => { |
| 83 | + // This is the function to run when the dead letter (timed out) message is picked up |
| 84 | + var data = JSON.parse(msg.content.toString()); |
| 85 | + // Change the command and return back to the correct queue (replyTo) to be handled |
| 86 | + // Clear x-first-death-reason header |
| 87 | + msg.properties.headers["x-first-death-reason"] = null; |
| 88 | + // Set command to timeout to be handled when collected from the node's queue |
| 89 | + data.command = "timeout"; |
| 90 | + // Resend message, this time to the reply queue for the correct node (replyTo) |
| 91 | + this.SendMessage(JSON.stringify(data), msg.properties.replyTo, msg.properties.correlationId, false); |
| 92 | + }, |
| 93 | + { noAck: true }); |
| 94 | + |
| 95 | + // Need to assert new queue first to ensure it has the timeout arguments added to it |
| 96 | + await this.channel.assertQueue(queue, { |
| 97 | + durable: false, |
| 98 | + arguments: { |
| 99 | + 'x-dead-letter-exchange': Config.amqp_dlx_prefix + queue, |
| 100 | + 'x-dead-letter-routing-key': Config.amqp_dlrk_prefix + queue, |
| 101 | + 'x-message-ttl': Config.amqp_message_ttl |
| 102 | + } |
| 103 | + }); |
| 104 | + |
72 | 105 | this.channel.sendToQueue(queue, Buffer.from(msg), { correlationId: correlationId, replyTo: this._ok.queue }); |
73 | 106 | } else { |
74 | 107 | this.channel.sendToQueue(queue, Buffer.from(msg), { correlationId: correlationId }); |
|
0 commit comments