The following is the exception i receive .Net core and rabbitMQ :
The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=405, text='RESOURCE_LOCKED - cannot obtain exclusive access to locked queue 'demo-queue' in vhost '/'. It could be originally declared on another connection or the exclusive property value does not match that of the original declaration.', classId=50, methodId=10
Producer :
var factory = new ConnectionFactory
{
Uri = new Uri("amqp://guest:guest#localhost:5672")
};
using var connection= factory.CreateConnection();
using var channel= connection.CreateModel();
channel.QueueDeclare("demo-queue",durable:true,exclusive:true,autoDelete:false, arguments:null);
var message = new
{
Name = "Producer",
Message = "Hello!"
};
var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));
channel.BasicPublish("", "demo-queue",null,body);
Consumer:
var factory = new ConnectionFactory
{
Uri = new Uri("amqp://guest:guest#localhost:5672")
};
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.QueueDeclare("demo-queue", durable: true,
exclusive: true, autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (sender, e) =>
{
var body = e.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(message);
};
channel.BasicConsume("demo-queue",true,consumer);
Is it the right practice or right way IF I Change the name of the queue that I have given in producer or consumer? which is the right way to handle this?
In the QueueDeclare section, you said properties that are different from the queue you created.
Edit:
All your mistakes are in this line of code. Because you created a queue. Now you want to open it with the wrong properties. I think exclusive: false will solve the problem
//channel.QueueDeclare("demo-queue",durable:true,exclusive:true,autoDelete:false, arguments:null);
channel.QueueDeclare(queue: "demo-queue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
Exchange and queu need to be connected. If you do not do this, the message cannot know the queue address it will go to. After QueueDeclare, it must be bind with the exchange address. I have implemented it this way. If you want, you can look at the repo I created here.
https://github.com/oguzhanKomcu/RabbitMQ_Sample
I think this is the missing function.
public void BindQueu(string exName, string queuName, string routingKey)
{
channel.QueueBind(queuName,exName, routingKey);
}
Related
I have my sender class.
public class RabbitSender
{
private ConnectionFactory _factory;
private IConnection _connection;
private IModel _channel;
public RabbitSender()
{
_factory = new ConnectionFactory() { HostName = "localhost" };
_connection = _factory.CreateConnection();
_channel = _connection.CreateModel();
_channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
}
public void Send(SendModel print)
{
byte[] messageBody = print.ToByteArray();
_channel.BasicPublish(exchange: "",
routingKey: "hello",
basicProperties: null,
body: messageBody);
}
public void Send2(SendModel print)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
byte[] messageBody = print.ToByteArray();
channel.BasicPublish(exchange: "",
routingKey: "hello",
basicProperties: null,
body: messageBody);
}
}
}
Method Send2() according the documetation on the website of RabbitMQ. As u can see it is using
using and with it I dont have problem with memory leak. But in this case I always create new connection when call method Send2().
In case of Send() I create conection once but I did not use using in this case. Because using always close my _channel and I cant send next msg.
I try to call _channel.Dispose() after sending but it is the same result such with using. And the main problem if I will not release garbage collector my memory is leaking. In 30 minutes I use 86% of my memory :/. In case Send2() I dont have this problem. How I can realese once creation of connection and after each sending to clean up the garbage without closing _channel?
I think there are probably several things going on, but the core issue is that the RabbitMQ client is not being used correctly per the documentation.
Connections are expensive, and should be created once per application instance. Keep the connection object around and re-use it when you need to create a new channel.
Channels are intended to be transient. You do not need to keep channels around - they can be disposed of after each operation. If you are doing a lot of publishes, it may be best to keep the channel around after each publish in a similar fashion as you keep the connection.
You'll need some logic to reconnect/re-establish your application when the connection to the server is lost.
Looking at the example code from Rabbit MQ's site for a consumer...
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
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);
};
channel.BasicConsume(queue: "hello",
autoAck: true,
consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
It initially looked like the messages were coming off the queue 1 by 1 in order and handled consecutively by the code inside the Received section.
However the result I am now seeing suggests that they are probably coming off 1 by 1 in order, but being handle concurrently, is this correct?
Regards
Tom
Using RabbitMQ it is possible to control how many messages that should be processed concurrent by calling:
channel.BasicQos(0, <MaxConcurrentConsumerThreads>, false);
So if chronology is important calling channel.BasicQos(0, 1, false); ensures only 1 message is processed at a time.
At The moment im learning how to work with the RabbitMQ.
Sending works. But Recieving doesn't work. This is my code:
var factory = new ConnectionFactory() { HostName = hostName };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: queueName,
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine("Recieved: {0}", message);
};
consumer.Shutdown += (o, e) =>
{
Console.WriteLine("Error with RabbitMQ: {0}", e.Cause);
createConnection(hostName, queueName);
};
channel.BasicConsume(queueName, true, consumer);
}
This is copied from the Tutorial. If I start the Application, consumer.Shutdown is directly called and I get:
{AMQP close-reason, initiated by Application, code=200, text="Goodbye", classId=0, methodId=0, cause=}
Can anyone help me?
channel.BasicConsume is non-blocking call, which means it will return immediately. What happens next in your example is your channel and connection are getting disposed (because of using statement), and so you see immediate shutdown. In the example you copied this code from, there is Console.ReadLine statement right after channel.BasicConsume. This prevents channel and connection from disposing until user press key in console.
Is there a good example (C#) of how to do the direct reply-to in RabbitMQ? What I want to do is for X Producers to post a message ("I've got some work for somebody") and I want one of X Consumers to pick it up, do the work and send the response back. Not a basic Ack, but some data, the result of the calculation. Of course, the response has to go back to the right producer.
Producer:
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "hello",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
string message = "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
var properties = channel.CreateBasicProperties();
properties.Persistent = true;
channel.BasicPublish(exchange: "",
routingKey: "hello",
basicProperties: properties,
body: body);
Console.WriteLine(" [x] Sent {0}", message);
}
}
Consumer:
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "hello",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
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);
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
};
channel.BasicConsume(queue: "hello",
noAck: false,
consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
It's not very clear from the minimal docs on how to set up both sides. I know somebody has to do something with the "amq.rabbitmq.reply-to" queue, but its not clear which side and what they have to do with it.
Have you seen the tutorials on the rabbitmq website? http://www.rabbitmq.com/tutorials/tutorial-six-dotnet.html
You would set up your code the same way as the RPC example, above, with only a few minor differences (noted in the docs you've referenced: https://www.rabbitmq.com/direct-reply-to.html).
When publishing a message from the original message producer, set the "replyTo" to amq.rabbitmq.reply-to
Have the original message producer also be a message consumer, consuming from the amq.rabbitmq.reply-to queue
When the code that handles the original request is done processing, you will publish a message from that worker, through the default (empty, no-name, "") exchange, with the routing key also set to amq.rabbitmq.reply-to
So:
client begins consuming messages from amq.rabbitmq.reply-to queue
client sends request for work, with amq.rabbitmq.reply-to as the replyTo property
worker picks up message, does work, publishes a response through the "" exchange, using amq.rabbitmq.reply-to as the routing key
that should be about it
I am beginner to RabbitMQ integration. I was doing some experiment with RabbitMq and making it as task runner.
For eg:
Let say I have a class Tasks, which has a method called Foo()
public static string Foo(string test, int id)
{
return "Admin" + test + id.ToString();
}
I have another class called Producer, which is declaring Queue for RabbitMQ.
using (var conn = factory.CreateConnection())
{
using (var channel = conn.CreateModel())
{
channel.QueueDeclare(queue: "KKQueue", durable: false, exclusive: false, autoDelete: false, arguments: null);
var message = "Hello World";
channel.BasicPublish(exchange: string.Empty, routingKey: "KKQueue", basicProperties: null, body: Encoding.UTF8.GetBytes(message ));
Console.WriteLine(" [x] Sent {0}", message);
}
Console.WriteLine(" Press [enter] to exit.");
}
Here we have to pass string as message to consumer.
Consumer class code will consume this queue message
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "KKQueue", durable: false, exclusive: false, autoDelete: false, arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var message = Encoding.UTF8.GetString(ea.Body);
Console.WriteLine(" [x] Received {0}", message);
};
channel.BasicConsume(queue: "KKQueue", noAck: true, consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
which is accepting string message. Is there a way, by which I can pass put my function call to Producer or Rabbit MQ Queue and let Consumee execute those function calls which are in queue.
I tried by serializing/deserializing the object and then using reflection to invoke the method in consumer code..I am looking for some alternate solution to it by using delegate or something. Any help will be greatly appreciated.
No, you can't. You need to "ship" all the assembly code with the message, which is definitively a very bad idea.
You should add you task code library to you consumer app and then use the message to govern the task execution, but without sending the executable code with it.
I personally use MassTransit for this kind of producer/consumer scenario; for example you can have multiple consumers, one for each task, and you can activate the right consumer simply sending a different type of message through RMQ.