Queue is locking even though it is declared as non-exclusive (RabbitMQ) - c#

I'm using the .NET client for RabbitMQ to implement the RPC pattern for a client and server. The client and server both declare the queues using the same parameters so that messages sent before the server is up won't be lost and vice versa. However, declaring the queue on the client side throws an OperationInterruptedException:
operation queue.declare caused a channel exception resource_locked: cannot obtain exclusive access to locked queue '151' in vhost '/'. It could be originally declared on another connection or the exclusive property value does not match that of the original declaration.
This question is similar to RabbitMQ: ACCESS_REFUSED even if the queue is non-exclusive, but the answer there did not work for me. The BasicConsumer defaults to being non-exclusive in the .NET client. If I declare the queue only in the server, the code works without issues. I don't want to have to make sure the server starts before the client, though.
What is causing this exception? Neither queue is declared as exclusive.
(Both code samples have been compressed for brevity.)
Client:
public MyClient(){
try {
ConnectionFactory factory = new() {
HostName = "localhost"
};
IConnection connection = factory.CreateConnection();
channel = connection.CreateModel();
sendUpdatesMessenger = new("sendUpdates", channel);
getUpdatesMessenger = new("getUpdates", channel);
getUpdatesReceiver = new("getUpdates", "localhost", channel);
sendUpdatesReceiver = new("sendUpdates", "localhost", channel);
string bodyStr = "test example string";
string messageId = new Guid().ToString();
bool success = getUpdatesMessenger.SendMessage(messageBody: bodyStr, routingKey: "151", correlationId: messageId, replyQueue: "myReplyQueue");
//Do stuff based on success or failure
} catch (Exception ex){
Debug.WriteLine("Exception " + ex);
}
}
public bool SendMessage(string messageBody, string routingKey, string correlationId, string replyQueue = ""){
try{
IBasicProperties props = channel.CreateBasicProperties();
props.CorrelationId = correlationId;
props.ReplyTo = replyQueue;
channel.QueueDeclare(queue: routingKey, durable: true, exclusive: false, autoDelete: false);
channel.BasicPublish(exchange: exchange, routingKey: routingKey, basicProperties: props, body: Encoding.ASCII.GetBytes(messageBody));
return true;
}
catch(Exception ex){
Debug.WriteLine("Error: " + ex);
return false;
}
}
Server:
public bool BindQueue(string routingKey){
try {
channel.QueueDeclare(queue: routingKey, durable: true, autoDelete: false);
channel.QueueBind(queue: routingKey, routingKey: routingKey, exchange: exchangeName);
queueNames.Add(routingKey);
return true;
}
catch (Exception ex){
Console.WriteLine(ex);
return false;
}
}

Related

Connect to AWS MQTT

Trying to connect to AWS IOT device from C# by using M2MQTT library :
public void connect()
{
const string IotEndpoint = "a3cnel9bxxxxx-ats.iot.eu-central-1.amazonaws.com";
const int BrokerPort = 8883;
const string Topic = "topic_1";
X509Certificate clientCert = X509Certificate2.CreateFromCertFile("C:\\cpp_test\\certs\\pi4\\certificate.pem.crt");
X509Certificate caCert = X509Certificate.CreateFromCertFile("C:\\cpp_test\\certs\\Amazon-root-CA-1.pem");
MqttClient client = new MqttClient(IotEndpoint, BrokerPort, true, caCert, clientCert, MqttSslProtocols.TLSv1_2);
String message = "Test message";
string clientId = Guid.NewGuid().ToString();
try
{
client.Connect(clientId);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
if (ex.InnerException != null)
MessageBox.Show("Inner exception: {0}", ex.InnerException.Message);
}
client.Publish(Topic, Encoding.UTF8.GetBytes(message));
}
Got exception:
Received an unexpected EOF or 0 bytes from the transport stream.
What might be wrong? Where is starting point of problem solving?
Getting no data is often a result of the SSL handshake failing or protocols not being supported, verify the certificates and protocols are correct.

Problem with RabbitMQ and deduplication plugin

I'm a RabbitMQ newbie; for a new project I need to use the deduplication plugin. I'm using AspNet Core 3.0 worker process and language is C#.
I've tried a very simple example, 2 publishers sending 10 messages numbered 1 to 10 and one consumer getting messages and acknowledging them.
I'm having quite strange and unpredictable results:
if I run the 3 workers (2 Publishers and one consumer) inside the same process, it looks like that deduplication plugin works fine and inserts in the queue only 10 unique messages, but the consumer reads only the first 2 and ackowledges only one of them.
if I run publishers and consumer in two different processes, the consumer gets all the 10 messages but after ack the messages remain in the queue and if I run again the consumer process they get reprocessed again.
I've tried to google for some full working sample in C# for deduplication, but without success
Publisher
int cnt = 1;
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
Dictionary<string, object> dd = new Dictionary<string, object>();
dd["x-message-deduplication"] = true;
channel.QueueDeclare(queue: qname,
durable: true,
exclusive: false,
autoDelete: false,
arguments: dd);
while (!stoppingToken.IsCancellationRequested)
{
var message = GetMessage(cnt);
var body = Encoding.UTF8.GetBytes(message);
var properties = channel.CreateBasicProperties();
properties.Persistent = true;
Dictionary<string, object> d = new Dictionary<string, object>();
d["x-deduplication-header"] = cnt;
properties.Headers = d;
channel.BasicPublish(exchange: "",
routingKey: qname,
basicProperties: properties,
body: body);
Console.WriteLine(" [x] Sent {0}", message);
logDB(cnt, "Sender"+Wname);
cnt++;
if (cnt > 10)
break;
await Task.Delay(1000, stoppingToken);
}
Consumer:
while (!stoppingToken.IsCancellationRequested)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
Dictionary<string, object> dd = new Dictionary<string, object>();
dd["x-message-deduplication"] = true;
channel.QueueDeclare(queue: qname,
durable: true,
exclusive: false,
autoDelete: false,
arguments: dd);
_logger.LogInformation("{0} Waiting for messages.", Cname);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
_logger.LogInformation("{0} Received {1}", Cname, message);
string[] parts = message.Split('-');
int cntmsg = int.Parse(parts[1]);
logDB(cntmsg, Cname);
Thread.Sleep((cntmsg % 5) * 1000);
_logger.LogInformation("{0} Received {1} done", Cname, message);
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: true);
};
channel.BasicConsume(queue: qname,
autoAck: false,
consumer: consumer);
_logger.LogInformation("{0} After BasicConsume", Cname);
while (true)
await Task.Delay(1000, stoppingToken);
}
after contacting the developer of the deduplication plugin it turned out the problem was related to the type (int) of deduplication header, using a string value works.
He will release a new version supporting int datatype soon.

How to make an HTTP Get request along with SslStream & TcpClient

In my application I am using the below code to validate the client certificate
public static async Task<string> CallApi(string url, Context context)
{
var hostName = "mytestapp.azurewebsites.net";
var port = 443;
Stream keyin = Application.Context.Assets.Open("Server.pfx");
var password = "pass123";
using (MemoryStream memStream = new MemoryStream())
{
keyin.CopyTo(memStream);
var certificates = new X509Certificate2Collection(new X509Certificate2(memStream.ToArray(), password));
await Task.Run(() =>
{
// Create a TCP/IP client socket.
// machineName is the host running the server application.
TcpClient client = new TcpClient(hostName, port);
Console.WriteLine("Client connected.");
// Create an SSL stream that will close the client's stream.
SslStream sslStream = new SslStream(
client.GetStream(),
false,
ValidateServerCertificate);
// The server name must match the name on the server certificate.
try
{
sslStream.AuthenticateAsClient(hostName, certificates, SslProtocols.Tls12, true);
}
catch (AuthenticationException e)
{
Console.WriteLine("Exception: {0}", e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
return;
}
});
}
return string.Empty;
}
after successful authentication, I would like to make and HTTP get request.
sslStream.AuthenticateAsClient(hostName, certificates, SslProtocols.Tls12, true);
After this statement. Say for example I need to call below http Get call
https://mytestapp.azurewebsites.net/api/GetUserProfile?userId="Sooraj"
How I can invoke this call? Or is it possible to implement the same?
Please help
Try something like this,
try
{
sslStream.AuthenticateAsClient(hostName, certificates, SslProtocols.Tls12, true);
byte[] buffer = new byte[5120];
int bytes;
var pqr = string.Format("GET {0} HTTP/1.1\r\nHost: {1}\r\n\r\n", url, "mytestapp.azurewebsites.net");
byte[] request = Encoding.UTF8.GetBytes(pqr);
sslStream.Write(request, 0, request.Length);
var ppp = ReadStream(sslStream);
sslStream.Flush();
}
catch (AuthenticationException e)
{
Console.WriteLine("Exception: {0}", e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
return;
}
private static string ReadStream(Stream stream)
{
byte[] resultBuffer = new byte[2048];
string value = "";
//requestStream.BeginRead(resultBuffer, 0, resultBuffer.Length, new AsyncCallback(ReadAsyncCallback), new result() { buffer = resultBuffer, stream = requestStream, handler = callback, asyncResult = null });
do
{
try
{
int read = stream.Read(resultBuffer, 0, resultBuffer.Length);
value += Encoding.UTF8.GetString(resultBuffer, 0, read);
if (read < resultBuffer.Length)
break;
}
catch { break; }
} while (true);
return value;
}
It will give you the response with all set of data. and later we need to parse the required information.

Long running RabbitMQ connection in ASP.NET

I know that establishing a RabbitMQ connection is expensive, so it is not recommended to {connect / publish / disconnect} whenever I want to put a message to queue. However, I couldn' t find a way to have a long running RabbitMQ connection across many requests.
What I want to achieve is to have only one(or a limited # of) RabbitMQ connection(s) in my ASP.NET application that runs on IIS and use it for different requests, instead of creating different connections for each request.
The following code is what I have up to now. I need somehow to remove these using statements and keep my connection open.
Thanks a lot
public ReturnCode AtomicPublish(string userName, string password, string virtualHost, string hostName, string exchangeName, string queueName, string message)
{
using (IConnection conn = new ConnectionFactory()
{
UserName = userName,
Password = password,
VirtualHost = virtualHost,
HostName = hostName
}.CreateConnection())
{
using (IModel model = conn.CreateModel())
{
model.ExchangeDeclare(exchangeName, ExchangeType.Fanout, true);
model.QueueDeclare(queueName, true, false, false, null);
model.QueueBind(queueName, exchangeName, "", null);
byte[] messageBodyBytes = System.Text.Encoding.UTF8.GetBytes(message);
model.BasicPublish(exchangeName, string.Empty, null, messageBodyBytes);
}
}
return ReturnCode.OK;
}
you should use server side storage for your variable connection,
if connection is same for all requests of same user save it in the Session objerct of ASP.NET,
if it is same for all users save it in the Cache application object.
then when you need to load it again and re-use it, get it back from Session or Cache, see here an example: Application vs Session vs Cache
off the top of my head.
private static IConnection _connection {get;set;}
private static object LockObject = new object();
private static IConnection GetConnection (string username, string password, string virtualHost, string hostName)
get{
// do work here in case the connection is closed as well.
if (_connection == null){
lock(LockObject){
if (_connection == null){
_connection = new ConnectionFactory
{
UserName = userName,
Password = password,
VirtualHost = virtualHost,
HostName = hostName
}.CreateConnection();
}
}
}
return _connection;
}
}
public ReturnCode AtomicPublish(string userName, string password, string virtualHost, string hostName, string exchangeName, string queueName, string message)
{
using (IModel model = GetConnection(userName, password, virtualHost, hostName).CreateModel()) //lazy loads the get connection
{
model.ExchangeDeclare(exchangeName, ExchangeType.Fanout, true);
model.QueueDeclare(queueName, true, false, false, null);
model.QueueBind(queueName, exchangeName, "", null);
byte[] messageBodyBytes = System.Text.Encoding.UTF8.GetBytes(message);
model.BasicPublish(exchangeName, string.Empty, null, messageBodyBytes);
}
return ReturnCode.OK;
}
Like you said, establishing a RabbitMQ connection is expensive, so when you open a connection you shouldn't send a message and disconnect.
In fact, RabbitMQ is based on the protocole AMQP which gives the possibility to send many messages to many receivers in different topics(instead of using queues), which means that every receiver is listening in a different topic like this exemple :
using System;
using System.Linq;
using RabbitMQ.Client;
using System.Text;
class EmitLogTopic
{
public static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "topic_logs",
type: "topic");
var routingKey = (args.Length > 0) ? args[0] : "anonymous.info";
var message = (args.Length > 1)
? string.Join(" ", args.Skip( 1 ).ToArray())
: "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "topic_logs",
routingKey: routingKey,
basicProperties: null,
body: body);
Console.WriteLine(" [x] Sent '{0}':'{1}'", routingKey, message);
}
}
}
this link provides a tutorial for more informations.

RabbitMQ and SharedQueue closed

Im using RabbitMQ to send simple short int information, first I'm sending id to one project like that:
private void SendPgcIdToRabbitMQ(string id)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
bool durable = true;
channel.QueueDeclare("XQueue", durable, false, false, null);
var body = Encoding.UTF8.GetBytes(id);
channel.BasicPublish("", "XQueue", null, body);
Console.WriteLine(" [x] Sent {0}", id);
}
}
}
and listener of it:
public void Listener()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare("XQueue", true, false, false, null);
var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume("XQueue", false, consumer);
Console.WriteLine(" [*] Waiting for messages. " +
"To exit press CTRL+C");
while (true) {
var ea =
(BasicDeliverEventArgs)consumer.Queue.Dequeue();
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] Received {0}", message);
AddPGCFileID(message);
channel.BasicAck(ea.DeliveryTag, false);
Thread.Sleep(500);
}
}
}
}
It works fine, so after receiving message I'm processing some operation wit it, then I get second ID and create other queue to do this same:
private void SendSurveyIdToRabbitMQ(int yID)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection()) {
using (var channel = connection.CreateModel()) {
bool durable = true;
channel.QueueDeclare("YQueue", durable, false, false, null);
var body = Encoding.UTF8.GetBytes(yID.ToString());
channel.BasicPublish("", "YQueue", null, body);
Console.WriteLine(" [x] Sent {0}", yID);
}
}
}
and receive:
public void InquiryListener()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection()) {
using (var channel = connection.CreateModel()) {
channel.QueueDeclare("YQueue", true, false, false, null);
var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume("YQueue", false, consumer);
Console.WriteLine(" [*] Waiting for messages. " +
"To exit press CTRL+C");
while (true) {
var ea =
(BasicDeliverEventArgs)consumer.Queue.Dequeue();
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] Received {0}", message);
StartProcessing(Convert.ToInt32(message));
channel.BasicAck(ea.DeliveryTag, false);
Thread.Sleep(500);
}
}
}
}
First queue sending and receiving works fine but at second I get:
It is strange because it was working that way, from some time I'm geting this problem. I whas reseting rabbitmq, removin all queues etc. can't find where is a problem. Any ideas?
edit:
I whas debuging to know if second process is ending properly (eariel crash on second proces don't cause problem with rabbitmq) and it passed, I whas supriced because no error ocurs on YQueue, but after about minute of working my process (only waiting, non incomming message, non processing) I gey this same exception on XQueue
Check first if the queue is empty before executing while(true){ ... }.

Categories

Resources