Since RabbitMQ (and AMQP) have no specialized functionality for delay adding a message to the queue a dead-letter exchange can be used instead. This works by having two separate exchanges, one "worker exchange" and one "delay exchange", and one queue for each exchange. For each exchange and queue pair a binding has to be setup that specifies the same routing key for both exchanges.
The consumer listens to the worker queue and any delayed message is added to the delay queue with the specified routing key and a expiration time. Instead of per-message expiration the delay queue can also specify a x-message-ttl that applies to all messages going in to it. When the message expires it will be reaped by RabbitMQ and put into the specified dead-letter exchange, the worker exchange in this case, and the routing key will direct it to the worker queue for consumption.