Here is what I have so far. I've been able to successfully publish a message to a queue and see that it is there via RabbitMQ's management console.
However, when I try to receive it, it does not seem to trigger the callback function at all.
Here is the relevant code.
MessageQueue mq = new MessageQueue();
mq.Receive("task_queue", (model, ea) => {
var message = Encoding.UTF8.GetString(ea.Body);
System.Diagnostics.Debug.WriteLine(message);
});
Here is my Receive function in the MessageQueue class:
public void Receive(string queueName, EventHandler<BasicDeliverEventArgs> onReceived)
{
using (IConnection connection = GetConnection(myLocalhost))
{
using (IModel channel = connection.CreateModel())
{
channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
// Don't dispatch a new message to a consumer until it has processed and acknowledged the previous one.
channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
var consumer = new EventingBasicConsumer(channel); // non-blocking
// Set the event to be executed when receiving a message
consumer.Received += onReceived;
// Register a consumer to listen to a specific queue.
channel.BasicConsume(queue: queueName, autoAck: true, consumer: consumer);
}
}
}
When I try to run the Receive function while there is something in the queue, nothing is printed to my output window.
Can anyone help me on this?
UPDATE
I took the code in the Receive function and placed it in the same file as the code that calls it. Still no luck. That rules out a scoping issue I think. I also tried setting the Received field to an actual event delegate (instead of a onReceive function and had that call another function in which I put a breakpoint. That function is never hit leading me to believe that my event delegate callback is never being called at all.
I'm at a loss as to why this is. The message is still being consumed from the queue as the RabbitMQ management console shows me. I've also tried renaming the queue to something else to make sure no other phantom services are consuming from the same queue. No cigar.
UPDATE 2
I tried extracting the two using statements and calling my Receive function inside there in order to keep the scope but that didn't work either. I even extracted the code in the whole Receive block out to a main function and now it doesn't even consume from the queue.
Looking at your code above, you have a pretty straightforward problem.
The instant after you call channel.BasicConsume, the whole thing (connection/channel) goes out of scope and is immediately disposed/destroyed via the using statement.
To prevent this from happening, you need to have an infinite loop immediately following the channel.BasicConsume, with appropriate logic of course to exit when you shut down the program.
while (_isRunning & channel.IsOpen) {
Thread.Sleep(1);
// Other application logic here; e.g. periodically break out of the
// loop to prevent unacknowledged messages from accumulating in the system
// (if you don't, random effects will guarantee that they eventually build up)
}
Related
I have created a solution based on Azure Functions and Azure Service Bus, where clients can retrieve information from multiple back-end systems using a single API. The API is implemented in Azure Functions, and based on the payload of the request it is relayed to a Service Bus Queue, picked up by a client application running somewhere on-premise, and the answer sent back by the client to another Service Bus Queue, the "reply-" queue. Meanwhile, the Azure Function is waiting for a message in the reply-queue, and when it finds the message that belongs to it, it sends the payload back to the caller.
The Azure Function Activity Root Id is attached to the Service Bus Message as the CorrelationId. This way each running function knows which message contains the response to the callers request.
My question is about the way I am currently retrieving the messages from the reply queue. Since multiple instances can be running at the same time, each Azure Function instance needs to get it's response from the client without blocking other instances. Besides that, a time out needs to be observed. The client is expected to respond within 20 seconds. While waiting, the Azure Function should not be blocking other instances.
This is the code I have so far:
internal static async Task<(string, bool)> WaitForMessageAsync(string queueName, string operationId, TimeSpan timeout, ILogger log)
{
log.LogInformation("Connecting to service bus queue {QueueName} to wait for reply...", queueName);
var receiver = new MessageReceiver(_connectionString, queueName, ReceiveMode.PeekLock);
try
{
var sw = Stopwatch.StartNew();
while (sw.Elapsed < timeout)
{
var message = await receiver.ReceiveAsync(timeout.Subtract(sw.Elapsed));
if (message != null)
{
if (message.CorrelationId == operationId)
{
log.LogInformation("Reply received for operation {OperationId}", message.CorrelationId);
var reply = Encoding.UTF8.GetString(message.Body);
var error = message.UserProperties.ContainsKey("ErrorCode");
await receiver.CompleteAsync(message.SystemProperties.LockToken);
return (reply, error);
}
else
{
log.LogInformation("Ignoring message for operation {OperationId}", message.CorrelationId);
}
}
}
return (null, false);
}
finally
{
await receiver.CloseAsync();
}
}
The code is based on a few assumptions. I am having a hard time trying to find any documentation to verify my assumptions are correct:
I expect subsequent calls to ReceiveAsync not to fetch messages I have previously fetched and not explicitly abandoned.
I expect new messages that arrive on the queue to be received by ReceiveAsync, even though they may have arrived after my first call to ReceiveAsync and even though there might still be other messages in the queue that I haven't received yet either. E.g. there are 10 messages in the queue, I start receiving the first few message, meanwhile new messages arrive, and after I have read the 10 pre-existing messages, I get the new messages too.
I expect that when I call ReceiveAsync for a second time, that the lock is released from the message I received with the first call, although I did not explicitly Abandon that first message.
Could anyone tell me if my assumptions are correct?
Note: please don't suggest that Durable Functions where designed specifically for this, because they simply do not fill the requirements. Most notably, Durable Functions are invoked by a process that polls a queue with a sliding interval, so after not having any requests for a few minutes, the first new request can take a minute to start, which is not acceptable for my use case.
I would consider session enabled topics or queues for this.
The Message sessions documentation explains this in detail but the essential bit is that a session receiver is created by a client accepting a session. When the session is accepted and held by a client, the client holds an exclusive lock on all messages with that session's session ID in the queue or subscription. It will also hold exclusive locks on all messages with the session ID that will arrive later.
This makes it perfect for facilitating the request/reply pattern.
When sending the message to the queue that the on-premises handlers receive messages on, set the ReplyToSessionId property on the message to your operationId.
Then, the on-premises handlers need to set the SessionId property of the messages they send to the reply queue to the value of the ReplyToSessionId property of the message they processed.
Then finally you can update your code to use a SessionClient and then use the 'AcceptMessageSessionAsync()' method on that to start listening for messages on that session.
Something like the following should work:
internal static async Task<(string?, bool)> WaitForMessageAsync(string queueName, string operationId, TimeSpan timeout, ILogger log)
{
log.LogInformation("Connecting to service bus queue {QueueName} to wait for reply...", queueName);
var sessionClient = new SessionClient(_connectionString, queueName, ReceiveMode.PeekLock);
try
{
var receiver = await sessionClient.AcceptMessageSessionAsync(operationId);
// message will be null if the timeout is reached
var message = await receiver.ReceiveAsync(timeout);
if (message != null)
{
log.LogInformation("Reply received for operation {OperationId}", message.CorrelationId);
var reply = Encoding.UTF8.GetString(message.Body);
var error = message.UserProperties.ContainsKey("ErrorCode");
await receiver.CompleteAsync(message.SystemProperties.LockToken);
return (reply, error);
}
return (null, false);
}
finally
{
await sessionClient.CloseAsync();
}
}
Note: For all this to work, the reply queue will need Sessions enabled. This will require the Standard or Premium tier of Azure Service Bus.
Both queues and topic subscriptions support enabling sessions. The topic subscriptions allow you to mix and match session enabled scenarios as your needs arise. You could have some subscriptions with it enabled, and some without.
The queue used to send the message to the on-premises handlers does not need Sessions enabled.
Finally, when Sessions are enabled on a queue or a topic subscription, the client applications can no longer send or receive regular messages. All messages must be sent as part of a session (by setting the SessionId) and received by accepting the session.
It seems that the feature can not be achieved now.
You can give your voice here where if others have same demand, they will vote up your idea.
This question is already exists and answered. But there is a dark side in answers. My channel already supports BasicAcks and BasicNacks handlers (in a poor way):
Channel.BasicAcks += (sender, eventArgs) =>
{
Console.WriteLine("Basic Ack!");
}
Channel.BasicNacks += (sender, eventArgs) =>
{
Console.WriteLine("Basic Nack!");
}
I have a message that published to a queue. so I use this code to do that:
Channel.BasicPublish("ExchangeName", "QueueName", messageProperties, payload);
Channel.WaitForConfirmOrDie();
As long as WaitForConfirmOrDie is a void function, how can I know if message received by a queue? Or more precise, how can I implement Ack handlers to give me a clear state of published message in order to not send it again to queue or in the case of BasicNack send it again?
Using the BasicAcks and BasicNacks event handlers is independent of calling Channel.WaitForConfirmOrDie.
Channel.WaitForConfirmOrDie is a convenience method that synchronously waits for message acknowledgements. So, if you publish messages one-by-one, you will wait for these acks one-by-one. As you can imagine, that is pretty inefficient.
What you should do is register for BasicAcks and BasicNacks like you have done. You should have an "acceptable number of outstanding confirms" defined. Here's one way to implement this -
Publish up to N messages without an ack/nack (N is up to you). If the next message would exceed N do not continue to publish messages.
While a message is outstanding, save it locally (in RAM or local disk). Remember that you can't be 100% sure a message is queued until you get an ack for it.
If the message is acked, remove it from local storage and decrease the count of outstanding messages, which allows publishing to continue (if publishing is blocked). Please remember that messages can be acked in batches.
If the message is nacked, you could re-try it up to a certain number of times, maybe with backoff. Once the re-try limit is exceeded, raise an application exception.
BACKGROUND INFO
I have a queue (for emails) in RabbitMQ, and want to build a consumer for it. The queue is used by another .NET app for sending emails to customers. I wanted the emailing logic to sit outside of the .NET app, and also have the benefits of durability ...etc that RabbitMQ offers.
ISSUE
The .NET app is able to publish/push emails onto the queue, but I have difficulty building the consumer! Here's my code for the consumer:
// A console app that would be turned into a service via TopShelf
public void Start()
{
using (_connection = _connectionFactory.CreateConnection())
{
using (var model = _connection.CreateModel())
{
model.QueueDeclare(_queueName, true, false, false, null);
model.BasicQos(0, 1, false);
var consumer = new EventingBasicConsumer(model);
consumer.Received += (channelModel, ea) =>
{
var message = (Email) ea.Body.DeSerialize(typeof(Email));
Console.WriteLine("----- Email Processed {0} : {1}", message.To, message.Subject);
model.BasicAck(ea.DeliveryTag, false);
};
var consumerTag = model.BasicConsume(_queueName, false, consumer);
}
}
}
The code above should be able to grab messages off the queue and process them (according to this official guide), but this isn't happening.
The problem is premature connection disposal. People often think that BasicConsume is a blocking call, but it is not. It will return almost immediately, and the very next statement is disposing (closing) of channel and connection which of course will cancel your subscription. So to fix - store connection and model in private fields and dispose them only when you are done with queue consumption.
You said queue is used by another .Net app, is that another consumer? If that is another consumer then can you please confirm which exchange you are using? If you want multiple consumers to pick up the message then please go ahead with "FanOut" exchange
I have a few questions on coding in RabbitMQ... I'm not new to this world and have got questions based on a design provided to me for implementation...
If I send BasicAck or BasicAack from Consumer, does it only remove
the corresponding message from the queue or will it deliver Ack to
the Publisher?
How do I ensure that Publisher sends message to Server only when Consumer is ready for processing?
The design says Publisher needs to wait and know when the processing of Consumer is completed to do certain task on client side (depending on success / failure).
I have tried below code but dequeue immediately removed message from queue without I send any Ack or Nack. I'm confused
publisher code:
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare("test", durable, false, false, null);
channel.TxSelect();
var properties = channel.CreateBasicProperties();
properties.SetPersistent(true);
string message = "Hello World!";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish("", "test", properties, body);
channel.TxCommit();
Console.WriteLine(" [x] Sent {0}", message);
}
}
Consumer code
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare("test", durable, false, false, null);
var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume("test", true, 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);
}
}
}
Note: I realized that channel.BasicConsume("test", true, consumer); has noAck to true. I changed this to channel.BasicConsume("test", false, consumer);
I can see the message is removed from the queue when I used channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false); But, how does Publisher know that the Consumer processed it successfully?
How do I ensure that Publisher sends message to Server only when Consumer is ready for processing?
You can't. And more important, you shouldn't. The point of using a messaging architecture is to forget about this kind of problems. Check this.
Also, RabbitMQ will store this messages for you until someone will be ready to process them if the queue is durable.
But, how does Publisher know that the Consumer processed it successfully?
No, will not. The ack is only between RabbitMQ and your consumer or between RabbitMQ and your producer. Check this for some details on ack/nack.
What you want to achieve here, if I can understand you correctly, is a kind of "chatty" architecture, where the consumer is also a producer of "response messages" for the publisher, messages like "hey, I'm done with message XX, everything is ok".
The easies way to do this is to set your consumer to be also a producer, and your producer to be also a consumer. You simply need to add a guid or an unique id of some sort to the message, and when you're done with it, you will send a message on another queue with that id as content to inform the original publisher (which is a consumer of this "response" queue) that the work has completed successfully.
Hope it helps :)
This is kind of a duplicated question. Check this thread, which is a similar question with a correct answer: Why doesn't Channel.waitForConfirmsOrDie block?
Or you can use the RPC pattern
https://www.rabbitmq.com/tutorials/tutorial-six-dotnet.html
Do anybody can solve my problem?
I wanna fire one event when some data is inserted in MSMQ queue how i can do it?
You create your instance of the message queue and start peeking at it asynchronously:
MessageQueue queue = new MessageQueue( #".\PRIVATE$\Queue" );
queue.BeginPeek( TimeSpan.Infinite, null, OnMessageAdded);
The registered callback is called when a message was added to the queue. As far as you did not explain what your real problem is, I assume you want to get informed about new messages added to the queue:
private void OnMessageAdded( IAsyncResult ar )
{
Message peekedMessage = queue.EndPeek(ar);
//Do whatever you want. Raise a new event, process the message, ...
}
The message is not taken off the queue. You can use the message, but it still remains in the queue. So if you don't need the Message, you can simply call queue.EndPeek(ar); without using the result.