30

I'm new to RabbitMQ and was wondering of a good approach to this problem I'm mulling over. I want to create a service that subscribes to a queue and only pulls messages that meet a specific criteria; for instance, if a specific subject header is in the message.

I'm still learning about RabbitMQ, and was looking for tips on how to approach this. My questions include: how can the consumer pull only specific messages from the queue? How can the producer set a subject header in the message (if that's even the right term?)

larryq
  • 15,713
  • 38
  • 121
  • 190
  • If really your consumer has to **subscribe** (not interested in messages being published _before_ the subscription), the subscription (and binding with selection rule) is not to an existing _queue_ but to an existing _exchange_ (as answered below).. Then, the question's wording should be edited. – Myobis Jan 12 '14 at 20:41

4 Answers4

37

RabbitMQ is perfect for this situation. You have a number of options to do what you want. I suggest reading the documentation to get a better understanding. I would suggest that you use a topic or direct exchange. Topic is more flexible. It goes like this.

Producer code connects to the RabbitMQ Broker and creates and Exchange with a specific name.

Producer publishes to exchange. Each message published will be published with a routing key.

Consumer connects to RabbitMQ broker.

Consumer creates Queue

Consumer binds Queue to the exchange, the same exchange defined in the producer. The binding also includes the routing keys for each message require for this particular consumer.

Lets say you were publishing log messages. The routing key might be something like "log.info", "log.warn", "log.error". Each message published by the producer will have the relevant routing key attached. You will then have a consumer that sends and email for all the error messages and another one that writes all the error messages to a file. So the emailer will define the binding from its queue to the exchange with the routing key "log.error". This way though the exchange receives all messages, the queue defined for the emailer will only contain the error messages. The filelogger will define a new separate queue bound to the same exchange and set up a different routing key. You could do three separate bindings for the three different routing keys require or just use the wildcard "log.*" to request all messages from the exchange starting with log.

This is a simple example that shows how you can achieve what you want to do.

look here for code examples specifically number tutorial number 5.

robthewolf
  • 7,343
  • 3
  • 29
  • 29
  • Do you mean consumer can retrieve message with specific routing-key in one queue? It looks like consumer don't have choice, it just receive all messages from queue. – ThemeZ Dec 27 '13 at 09:31
  • 3
    @ThemeZ consumers receive all messages from the queue. I think that is by definition. The point here is that they are filtered at the exchange level. With a topic exchange the queue will only receive certain messages. That way a consumer will on read the messages they want because the queue only receives the messages that the consumer wants. – robthewolf Dec 29 '13 at 14:53
  • 2
    I don't think this answers the question. The question is about having a single queue called "log" and the Consumer able to consume only 'info' logs from the queue. – Nick Jan 13 '20 at 12:48
  • 1
    In fairness the author wants to do something that queues are specifically not designed to do. I was merely proposing a solution that would work. – robthewolf Jan 13 '20 at 19:47
3

Making the best of exchange/routing of rabbitmq is recommended. If you do want to check according to the message content, the following code is a viable solution.

Retrieve messages from a queue and check, selectively ack the messages in which you're interested.

pull one message

GetResponse resp = channel.basicGet(QUEUE_NAME, false);

ack one message

channel.basicAck(resp.getEnvelope().getDeliveryTag(), false);

Example

import com.rabbitmq.client.*;

public class ReceiveLogs {
    private final static String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try(Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();){

            channel.queueDeclare(QUEUE_NAME, true, false, false, null);

            // pull one message and ack manually and exit
            GetResponse resp = channel.basicGet(QUEUE_NAME, false);
            if( resp != null ){
                String message = new String(resp.getBody(), "UTF-8");
                System.out.println(" [x] Received '" + message + "'");
                channel.basicAck(resp.getEnvelope().getDeliveryTag(), false);
            }
            System.out.println();
        }
    }
}

dependency

compile group: 'com.rabbitmq', name: 'amqp-client', version: '5.8.0'
Iceberg
  • 2,744
  • 19
  • 19
-3

To Retrieve Message from RabbitMQ we need to first connect with RabbitMQ server

public WebClient GetRabbitMqConnection(string userName, string password)
{
    var client = new WebClient(); 
    client.Credentials = new NetworkCredential(userName, password);
    return client;
}

Now retrieve message from RabbitMQ using below code.

public string GetRabbitMQMessages(string domainName, string port, 
    string queueName, string virtualHost, WebClient client, string methodType)
{
    string messageResult = string.Empty;
    string strUri = "http://" + domainName + ":" + port + 
                    "/api/queues/" + virtualHost + "/";
    var data = client.DownloadString(strUri + queueName + "/");
    var queueInfo = JsonConvert.DeserializeObject<QueueInfo>(data);
    if (queueInfo == null || queueInfo.messages == 0)
               return string.Empty;
    if (methodType == "POST")
    {
        string postbody = "  
        {\"ackmode\":\"ack_requeue_true\",\"count\":
         \"$totalMessageCount\",\"name\":\"${DomainName}\",
         \"requeue\":\"false\",\"encoding\":\"auto\",\"vhost\" :
         \"${QueueName}\"}";
         postbody = postbody
                       .Replace("$totalMessageCount", queueInfo.messages.ToString())
                       .Replace("${DomainName}", domainName)
                       .Replace("${QueueName}", queueName);
         messageResult = client.UploadString(strUri + queueName + 
                          "/get", "POST", postbody);
    }
    return messageResult;
} 

I think this will help you to implement RabbitMQ.

barbsan
  • 3,418
  • 11
  • 21
  • 28
  • 6
    Hello Punit. Welcome to StackOverflow. It seems like your answer doesn't answer the question. He was asking "how can the consumer **pull only specific messages** from the queue" and "how can the producer **set a subject header in the message**". Your answer seems to be about connecting to the `RabbitMQ` server and retrieving data in general. He wants to pull specific messages that may have headers in the message. Please consider revising your answer. Additionally, please take the time to reformat it as it appears that there are sections that aren't code, but are formatted as code. – Ishaan Javali Jan 13 '19 at 15:02
  • Hey bro Ishaan, this is rabbitmq you need to retrieve messages from perticular Queue to user define list then apply linq to filter your perticular message .... you need to apply your own logic .... All the best ! – Punit Pandya Apr 04 '19 at 09:05
-4

If you want to retrieve single message at a time please add the following properties with your Retrieving code .

Boolean autoAck = false;
model.BasicConsume(Queuename, autoAck);
model.BasicGet("Queuename", false);
model.BasicGet("Queuename", false); 

By adding this properties of RabbitMQ you can retrieve the message one by one from the queue .Same like FIFO criteria

  • 1
    Hi, this is quite far from answering the question. Also the existing answers provide better explanation on how to get specific messages out of a queue. – SwissCoder Oct 11 '19 at 09:31