Precisely Delaying Jobs With Dead-Letter Exchanges in RabbitMQ
19 Jul 2014Everyone who is familiar with RabbitMQ knows it’s a great tool to execute jobs that may happen out of band of some normal user process. Typically you want to execute these jobs right away, such as sending some kind of notification email, but there may be times in which you want the execution of these jobs to be delayed by specific times set by the client. RabbitMQ does not support this directly, but I encountered an interesting trick using Dead-Letter Exchanges to achieve this functionality.
How It Works
Instead of publishing directly to an exchange as we normally would, the producer creates a new, unique queue and publishes it’s message to it. In our code we are calling it send.later.<timestamp>
.
When we create this queue, we set some special options:
The x-message-ttl
is a time in milliseconds that you want to delay the job by. This can be unqiue per message. The x-expires
is the time in milliseconds you wish for that temporary queue to exist. The x-dead-letter-exchange
is the exchange you wish for the message to be published to after it expires. So, we are sortof doing a semantic hack here. Instead of actually expiring the message we are actually activating it on expiration. x-dead-letter-key
is the routing key to be used when publishing into the immediate
exchange.
We have previously created an exchange called immediate
in which the consumers are listening to run jobs from immediately. When the message expires
, it will be thrown into this exchange.
The Code
The code is written in node.js and uses node-amqp to speak to RabbitMQ. It is comprised of two parts (a producer and a consumer) which I have written into two separate programs.
The Producer (producer.js)
The Consumer (consumer.js)
Running
You’ll need node-amqp installed and you’ll need a rabbit server running on localhost with a default configuration.
In one terminall session run:
This will start listening for messages ready to be run and print them out to the screen.
In another terminal run:
This will publish a message every 1 second, but they won’t come out of the consumer end until 5 seconds after they were published.
Altering This Code
You don’t have to do this exactly as I have. For instance, I am using a topic exchange but I think this should work with any kind of exchange. You also could share all the send later messages in a single queue that never expires. It really depends on your use case how you wish to configure it.