RabbitMq single Consumer with multiple queue - rabbitmq

I want to create single consumer(Generic Listener) for multiple queues.The consumer should listen multiple queues.
Lets see in the example
channel.ExchangeDeclare(exchange: "logs", type: "fanout");
var queueName = "QeueueName.Instance1";
channel.QueueBind(queue: queueName,
exchange: "logs",
routingKey: "");
Console.WriteLine(" [*] Waiting for logs.");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] {0}", message);
};
I want to associate the consumer with dynamic no of queues and they will
increase time to time so how i will associate consumer to future created queues.I have created a window service for the same so do i have to loop all the queues and associate with consumer and for the future created queues I should add them in the consumer queue list.

When I first read your question I didn't think you could bind one consumer to multiple queues, but I just tried this and it works fine:
ConnectionFactory factory = new ConnectionFactory()
{
VirtualHost = "testHost1",
UserName = "guest",
Password = "guest",
Port = 5672,
};
var connection = factory.CreateConnection();
var channel = connection.CreateModel();
channel.ExchangeDeclare("testExchange1", ExchangeType.Fanout);
channel.QueueDeclare("testQueue1");
channel.QueueDeclare("testQueue2");
channel.QueueBind("testQueue1", "testExchange1", "");
channel.QueueBind("testQueue2", "testExchange1", "");
var consumer1 = new EventingBasicConsumer(channel);
consumer1.Received += Consumer1OnReceived;
channel.BasicConsume("testQueue1", false, consumer1);
channel.BasicConsume("testQueue2", false, consumer1);
Note that your code doesn't include a call to BasicConsume(). Your consumer won't receive anything without it.

Related

Failed to send message using MQ

On my server side I deployed RabbitMq message middleware using docker and it works perfectly. But I combine it with asp.net core, it can't send the message successfully.
package:https://www.nuget.org/packages/RabbitMQ.Client/
I goole some answers:
RabbitMQ adopts the message response mechanism, that is, after the
consumer receives a message, it needs to send a response, and then
RabbitMQ will delete the message from the queue. If the consumer has
an exception during the consumption process, the connection is
disconnected and no response is sent. Then RabbitMQ will redeliver the
message
So I modified my code
//message received event
consumer.Received += (ch, ea) =>
{
var message = Encoding.UTF8.GetString(ea.Body);
Console.WriteLine($"Received the news: {message}");
Console.WriteLine($"received the message[{ea.DeliveryTag}] delay 10s to send receipt");
Thread.Sleep(10000);
//Confirm that the message has been consumed
channel.BasicAck(ea.DeliveryTag, false);
Console.WriteLine($"Receipt sent[{ea.DeliveryTag}]");
};
Still failed to send and no response, please help me, thank you!
I have a demo of sending a message, you may refer to it, it may be helpful.
Controller:
[Route("test")]
public void Index()
{
try
{
var factory = new ConnectionFactory();
factory.VirtualHost = "/";
factory.HostName = "localhost";
factory.Port = 5672;
factory.UserName = "guest";//Default username guest
factory.Password = "guest";//Default password guest
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "test",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
string message = "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "",
routingKey: "test",
basicProperties: null,
body: body);
Console.WriteLine(" [x] Sent {0}", message);
}
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
catch (Exception ex)
{
Console.Write(string.Format("RabbitMQ connection error :{0}\n", ex.ToString()));
}
}
For the sake of convenience, I did not configure the connection information of rabbitmq here. I wrote it directly into the method, you can do it yourself.
Resullt:

Code to get single message from the queue in RabbitMQ using C#

How to receive single message from the queue in RabbitMQ using C#
I am using the RabbitMQ to maintain my Queue.When I am read my queue messages its shows all massage present in the queue.
I want to retrieve single message as per First come first out criteria.
Please provide me code to get the single message from the queue by using RabbitMQ
var connection = connectionFactory.CreateConnection();
var channel = connection.CreateModel();
// accept only one unack-ed message at a time
// uint prefetchSize, ushort prefetchCount, bool global
channel.BasicQos(0, 1, false);
MessageReceiver messageReceiver = new MessageReceiver(channel);
channel.BasicConsume("viewer_Queues", false, messageReceiver);
//channel.QueueDeclare("viewer_Queues", false, false, false, null);
var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume("viewer_Queues", true, consumer);
BasicDeliverEventArgs ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(message + " Received.");
Console.ReadLine();
I think you need something like this, from the website Tutorials in the worker Queues Section:
`using System;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
using System.Threading;
class Worker
{
public static void Main()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "task_queue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
Console.WriteLine(" [*] Waiting for messages.");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] Received {0}", message);
int dots = message.Split('.').Length - 1;
Thread.Sleep(dots * 1000);
Console.WriteLine(" [x] Done");
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
};
channel.BasicConsume(queue: "task_queue",
autoAck: false,
consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}`
the prefetch argument is important if you want to get only one message at a time you can read about it in the official website https://www.rabbitmq.com/

Getting RabbitMq to behave as I want (dead letter and re-queue on errors)

I have a simple rabbit set up that is currently doing what I want...
It publishes messages based on the type of message. Each type gets its own queue.
When messages are published they sit on the queue even if there is no consumer to consume them (sit there forever if no consumer arrives).
When a consumer is there (there is only one!) it eats the messages.
If for some reason it cannot handle a message (eg it gets a sub message before the parent has arrived) it nacks the message back onto the queue.
If it sees the same message six times it nacks the message.
This all works, but currently after six attempts it drops the message.
What I would like is for the message to pass to a 'dead letter queue' and after some time (say 5 mins) re-queue that message at the end of the particular queue it came from.
I am definitely cargo cult programing and I don't quite understand all the exchange/queue/binding/routing keys and other arcania involved... hand holding is appreciated!
public void PublishEntity<T>(T message) where T : class, ISendable
{
logger.Info($"publishing {message.UniqueId}");
var factory = new ConnectionFactory
{
HostName = appSettings.RabbitHostName,
UserName = appSettings.RabbitUsername,
Password = appSettings.RabbitPassword
};
try
{
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
Console.WriteLine($"Setting up queues for: {typeof(T).Name}");
channel.QueueDeclare($"App_{typeof(T).Name}",
true,
false,
false,
null);
var json = JsonConvert.SerializeObject(message);
var body = Encoding.UTF8.GetBytes(json);
channel.TxSelect();
var properties = channel.CreateBasicProperties();
properties.Persistent = true;
properties.Headers = new Dictionary<string, object>
{
{ "Id", Guid.NewGuid().ToString() }
};
channel.BasicPublish("",
$"App_{typeof(T).Name}",
properties,
body);
Data.MarkAsSent(message);
channel.TxCommit();
}
}
}
ISendable just make sure the message has some properties used in Data.MarkAsSent(message); to mark in a database where we have got too.
The receiver has a similar lump of code to handle each type. As I say this is working.
What do I need to do to add the dead letter queue things?
My attempt like so created the dead letter queues, but nothing ever moves to them.
public void PublishEntity<T>(T message) where T : class, ISendable
{
logger.Info($"publishing {message.UniqueId}");
var factory = new ConnectionFactory
{
HostName = appSettings.RabbitHostName,
UserName = appSettings.RabbitUsername,
Password = appSettings.RabbitPassword
};
try
{
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
Console.WriteLine($"Setting up queues for: {typeof(T).Name}");
channel.ExchangeDeclare("App.Dead.Letter", "direct", true);
var args = new Dictionary<string, object>
{
{ "x-dead-letter-exchange", "App.Dead.Letter" },
{
"x-dead-letter-routing-key", $"DLQ.App_{typeof(T).Name}"
}
};
channel.QueueDeclare($"App_{typeof(T).Name}",
true,
false,
false,
args);
channel.QueueDeclare($"DLQ.App_{typeof(T).Name}",
true,
false,
false,
null);
var json = JsonConvert.SerializeObject(message);
var body = Encoding.UTF8.GetBytes(json);
channel.TxSelect();
var properties = channel.CreateBasicProperties();
properties.Persistent = true;
properties.Headers = new Dictionary<string, object>
{
{ "Id", Guid.NewGuid().ToString() }
};
channel.BasicPublish("",
$"App_{typeof(T).Name}",
properties,
body);
Data.MarkAsSent(message);
channel.TxCommit();
}
}
}
In my reciever I have this magic
catch (Exception ex)
{
var attemptsToHandle = MarkFailedToHandleMessage(logId, ex);
if (attemptsToHandle > 5)
{
//If we have seen this message many times then don't re-que.
channel.BasicNack(ea.DeliveryTag, false, false);
return;
}
// re-que so we can re-try later.
channel.BasicNack(ea.DeliveryTag, false, true);
return;
}
Phew... lot of code. Thanks if you have made it this far....
I am asking what are the glaring issues in my code to make things fall to the dead letter queue.
and what extra do I need to add so that things in the dlq will bounce back to the main queue after some time.
Additionally this sets up a dlq for each types queue... is this required or should there be a single queue to hold the errored messages?
So I think I got this working as I intend. Hard to test all this stuff though!
public void PublishEntity<T>(T message) where T : class, ISendable
{
logger.Info($"publishing {message.UniqueId}");
var factory = new ConnectionFactory
{
HostName = appSettings.RabbitHostName,
UserName = appSettings.RabbitUsername,
Password = appSettings.RabbitPassword
};
try
{
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
Console.WriteLine($"Setting up queues for: {typeof(T).Name}");
// Declair dead letter queue for this type
channel.ExchangeDeclare("App.Dead.Letter", "direct", true);
var queueArgs = new Dictionary<string, object>
{
{ "x-dead-letter-exchange", "App" },
{
"x-dead-letter-routing-key", $"App_{typeof(T).Name}"
}
,{ "x-message-ttl", 30000 }
};
channel.QueueDeclare($"DLQ.App_{typeof(T).Name}",
true,
false,
false,
queueArgs);
channel.QueueBind($"DLQ.App_{typeof(T).Name}", "App.Dead.Letter", $"DLQ.App_{typeof(T).Name}", null);
// declair queue for this type
channel.ExchangeDeclare("App", "direct", true);
var args = new Dictionary<string, object>
{
{ "x-dead-letter-exchange", "App.Dead.Letter" },
{
"x-dead-letter-routing-key", $"DLQ.App_{typeof(T).Name}"
}
};
channel.QueueDeclare($"App_{typeof(T).Name}",
true,
false,
false,
args);
channel.QueueBind($"App_{typeof(T).Name}", "App", $"App_{typeof(T).Name}", null);
I added and exchange for my main queues to live in and actually bound the queues to the exchanges. I still don't know why I need to do this as it was working without this extra complexity.. I guess some magic was hiding it from me before?

Rabbitmq - how to listen to messages on an exchange

I have a program in Java that sends messages to RabbitMQ. All I know is the exchange name. No queues, bindings, and so on.
My question is this: how can I see if the program sends these successfully, knowing only the exchange name?
Thanks.
Regards,
Serban
You can enable publisher confirmation with RabbitMQ. It's like having a send-transaction, where RabbitMQ will tell you whether or not the message was sent successfully.
Assume that we have RabbitMQ Exchange we need to create an queue to push the message to the exchange and consume it from the queue as following
private static final String EXCHANGE_NAME = "2022";
private static final String QUEUE_NAME = "2022";
private final static boolean durable = true;
// now we need to create a connection to rabbitmq server //
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");
factory.setHost("127.0.0.1");
factory.setPort(5672);
Connection conn = factory.newConnection();
// create rabbitmq connection chaneel
Channel channel = conn.createChannel();
//Declare Exchange //
channel.exchangeDeclare(EXCHANGE_NAME, "topic", true);
// push message to rabbitmq exchange
channel.basicPublish(EXCHANGE_NAME, "routingkey", null, yourmessage.getBytes());
the above work as producer now we need to create queue consumer
private static final String EXCHANGE_NAME = "2022";
private static final String QUEUE_NAME = "2022";
private final static boolean durable = true;
// now we need to create a connection to rabbitmq server //
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");
factory.setHost("127.0.0.1");
factory.setPort(5672);
Connection conn = factory.newConnection();
// create rabbitmq connection chaneel
Channel channel = conn.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic", true);
//Queue Declare //
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//Queue bind //
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "routingkey");
// Queue Consume //
QueueingConsumer consumer = new QueueingConsumer(channel);
while (true)
{
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Received '" + message + "'");
}
Please look here: https://www.rabbitmq.com/tutorials/tutorial-three-java.html
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, "EXCHANGE_NAME", "");
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel) {
#Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException
{
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
In a few words, you have to:
create a queue, in this case anonymous queue
bind the queue to your exchange
It is important to know what kind of the exchange you have since the binding can change, between fanout or topic or direct
In this example is fanout

how to set the basic message properties for message in Rabbitmq?

i am using Rabbitmq Java client API.i want to set the Basic Properties for message and also get the message Id of the message.if possible please provide some code to understand the things.
Thanks
While sending a message through java client usually it is publish to a channel like
CHANNEL.basicPublish(EXCHANGE_NAME, QUEUE_ROUTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN, "message".getBytes)
Here you can set message properties
You can get the msg by using a delivery agent
You have to first bind the queue like this
Channel channel = conn.createChannel();
String exchangeName = "myExchange";
String queueName = "myQueue";
String routingKey = "testRoute";
boolean durable = true;
channel.exchangeDeclare(exchangeName, "direct", durable);
channel.queueDeclare(queueName, durable,false,false,durable, null);
channel.queueBind(queueName, exchangeName, routingKey);
boolean noAck = false;
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName, noAck, consumer);
Then use delivey to get msg
QueueingConsumer.Delivery delivery;
try {
delivery = consumer.nextDelivery();
} catch (InterruptedException ie) {
continue;
}
Here is how it can be done:
int PERSISTENCE_MESSAGE = 2; // Persist message
String TEXT_MESSAGE = "text/plain";
String queueName = "QUE-1";
Channel channel = this.connection.createChannel();
channel.queueDeclare(queueName, true, false, false, null);
// Build message properties
Map messageProps = new HashMap();
//messageProps.put("TIME_MSG_RECEIVED", time);
messageProps.put("SOURCE_SYS", "SRC1");
messageProps.put("DESTINATION_SYS", "DST1");
// Set message properties
AMQP.BasicProperties.Builder basicProperties = new AMQP.BasicProperties.Builder();
basicProperties.contentType(TEXT_MESSAGE).deliveryMode(PERSISTENCE_MESSAGE)
.priority(1).headers(messageProps);
channel.basicPublish("", queueName, basicProperties.build(), message.getBytes());
System.out.println(" Sent message to RabbitMQ: '" + message + "'");
channel.close();