4

In RabbitMQ,I have a failure queue, in which I have all the failed messages from different Queues. Now I want to give the functionality of 'Retry', so that administrator can again move the failed messages to their respective queue. The idea is something like that:

enter image description here

Above diagram is structure of my failure queue. After click on Retry link, message should move into original queue i.e. queue1, queue2 etc.

Ankush
  • 499
  • 1
  • 7
  • 17
Sumit Sood
  • 441
  • 7
  • 23
  • I don't know what you are looking for here. You have to write code to do this, and somewhere you're going to have to publish the message. – theMayer Feb 18 '18 at 03:32
  • And I don't know how many times you really want to try dividing by zero, but you're never going to get a different answer there – theMayer Feb 18 '18 at 03:33
  • Hi, I just want to move messages from one queue to another. I just wanted to check, if it's possible in rabbitMQ to move any message from one queue to another. I know that Shovel plugin would help but it move entire queue in another queue. I need to move them randomly one by one. If it's possible then I am looking Java implementation for that. – Sumit Sood Feb 18 '18 at 03:43
  • I think you need to get yourself familiar with DLX: https://www.rabbitmq.com/dlx.html – Artem Bilan Feb 18 '18 at 16:19
  • Not sure why this question is -1 – Sumit Sood Feb 20 '18 at 10:45

5 Answers5

2

If you are looking for a Java code to do this, then you have to simply consume the messages you want to move and publish those messages to the required queue. Just look up on the Tutorials page of rabbitmq if you are unfamiliar with basic consuming and publishing operations.

  • Is it possible to move message #2 from my failure queue without removing message #1? I haven't found any way to do that. – Sumit Sood Feb 20 '18 at 10:27
  • RabbitMQ is not designed in that way, if you know how a Queue is supposed to work, you will understand that. A producer produces messages into a queue and a consumer consumes messages from that queue, everything in FIFO way. So, what you are saying is that RabbitMQ should be implemented through a different data structure, which is not the current case. – Arpan Gupta Feb 20 '18 at 16:46
  • Thanks Arpan. the same thing which I was thinking. – Sumit Sood Feb 21 '18 at 06:11
1

It's not straight forward consume and publish. RabbitMQ is not designed in that way. it takes into consideration that exchange and queue both could be temporary and can be deleted. This is embedded in the channel to close the connection after single publish.

Assumptions: - You have a durable queue and exchange for destination ( to send to) - You have a durable queue for target ( to take from )

Here is the code to do so:

        import com.rabbitmq.client.Channel;
        import com.rabbitmq.client.QueueingConsumer;
        import org.apache.commons.lang.StringUtils;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
        import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;    

        public object shovelMessage(
                     String exchange,
                     String targetQueue,
                     String destinationQueue,
                     String host,
                     Integer port,
                     String user,
                     String pass,
                     int count) throws IOException, TimeoutException, InterruptedException {

                if(StringUtils.isEmpty(exchange) || StringUtils.isEmpty(targetQueue) || StringUtils.isEmpty(destinationQueue)) {
                    return null;
                }

                CachingConnectionFactory factory = new CachingConnectionFactory();
                factory.setHost(StringUtils.isEmpty(host)?internalHost.split(":")[0]:host);
                factory.setPort(port>0 ? port: Integer.parseInt(internalPort.split(":")[1]));
                factory.setUsername(StringUtils.isEmpty(user)? this.user: user);
                factory.setPassword(StringUtils.isEmpty(pass)? this.pass: pass);
                Channel tgtChannel = null;
                try {
                    org.springframework.amqp.rabbit.connection.Connection connection = factory.createConnection();

                    tgtChannel = connection.createChannel(false);
                    tgtChannel.queueDeclarePassive(targetQueue);

                    QueueingConsumer consumer = new QueueingConsumer(tgtChannel);
                    tgtChannel.basicQos(1);
                    tgtChannel.basicConsume(targetQueue, false, consumer);

                    for (int i = 0; i < count; i++) {
                        QueueingConsumer.Delivery msg = consumer.nextDelivery(500);
                        if(msg == null) {
        // if no message found, break from the loop.
                            break;
                        }
                        //Send it to destination Queue
                        // This repetition is required as channel looses the connection with 
                        //queue after single publish and start throwing queue or exchange not 
                        //found connection.
                        Channel destChannel = connection.createChannel(false);
                        try {
                            destChannel.queueDeclarePassive(destinationQueue);
    SerializerMessageConverter serializerMessageConverter = new SerializerMessageConverter();
     Message message = new Message(msg.getBody(), new MessageProperties());
                          Object o = serializerMessageConverter.fromMessage(message);
// for some reason msg.getBody() writes byte array which is read as a byte array // on the consumer end due to which this double conversion.
                            destChannel.basicPublish(exchange, destinationQueue, null, serializerMessageConverter.toMessage(o, new MessageProperties()).getBody());
                            tgtChannel.basicAck(msg.getEnvelope().getDeliveryTag(), false);
                        } catch (Exception ex) {
                            // Send Nack if not able to publish so that retry is attempted
                            tgtChannel.basicNack(msg.getEnvelope().getDeliveryTag(), true, true);
                            log.error("Exception while producing message ", ex);
                        } finally {
                            try {
                                destChannel.close();
                            } catch (Exception e) {
                                log.error("Exception while closing destination channel ", e);
                            }

                        }
                    }

                } catch (Exception ex) {
                    log.error("Exception while creating consumer ", ex);
                } finally {
                    try {
                        tgtChannel.close();
                    } catch (Exception e) {
                        log.error("Exception while closing destination channel ", e);
                    }
                }

                return null;

            }
Ankush
  • 499
  • 1
  • 7
  • 17
1

To requeue a message you can use the receiveAndReply method. The following code will move all messages from the dlq-queue to the queue-queue:

do {
    val movedToQueue = rabbitTemplate.receiveAndReply<String, String>(dlq, { it }, "", queue)
} while (movedToQueue)

In the code example above, dlq is the source queue, { it } is the identity function (you could transform the message here), "" is the default exchange and queue is the destination queue.

Sandro
  • 1,051
  • 7
  • 15
0

I also have implemented something like that, so I can move messages from a dlq back to processing. Link: https://github.com/kestraa/rabbit-move-messages

0

Here is a more generic tool for some administrative/supporting tasks, the management-ui is not capable of.

Link: https://github.com/bkrieger1991/rabbitcli

It also allows you to fetch/move/dump messages from queues even with a filter on message-content or message-headers :)

FluX
  • 1
  • 2