RabbitMQ stop waiting to messages - c#

I'm using RabbitMQ to provide consume items from queue. My application handled by Windows Service that most of the time running.
On service START (OnStart) I using the method
while (true)
{
var ea =(BasicDeliverEventArgs)consumer.Queue.Dequeue();
// ... Handle this item
}
In order to consume item from the queue. The function 'Dequeue()' is blocking. The Thread will be blocked in this line until some item will arrive.
My problem start when I'm trying to implement the OnStop method of my service. My target is to stop waiting for new items when OnStop signal arrived. So, I'm modified my code to something like this:
while (true)
{
if (this.IsStopping)
return; // OnStop signal arrived. Stop waiting.
var ea =(BasicDeliverEventArgs)consumer.Queue.Dequeue();
// ... Handle this item
}
In some cases, the code above is working fine. BUT, if the queue is empty, the execution of the Windows Service won't happen.
How do you recommend me to solve this problem?

you have two ways to do that, the first one is called "poison message", during the stop service you send a message to the queue:
while (true)
{
var ea =(BasicDeliverEventArgs)consumer.Queue.Dequeue();
// ... Handle this item
if (mymessage is MyPoisonMessage)
break;
}
Actually I don't i like, but it is a quick solution.
Another one is extend the class DefaultBasicConsumer and then use the consumer tag to close the consumer, something like that:
class SampleConsumer : DefaultBasicConsumer
{
public SampleConsumer(IModel channel) : base(channel)
{
}
public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey,
IBasicProperties properties, byte[] body)
{
.....
Then instance the class, get the consumerTag and close it in this way:
channel.BasicCancel(consumerTag)
hope it helps.

Related

C# How to force the wait for the connection to a WCF service

I have a service running as local SYSTEM that launches another application with the user credentials.
That second app is only a tray icon that shows balloon tips to the user with the string received using the callback method. This second application connects to the WCF in duplex mode.
My problem is that for some reason the connection to the WCF is finalized at the end of the method Main. So I cannot send a callback message to the app right after the execution, included in the last line "kiosk.MyStart(args);". there the callback is still pointing to null.
Any idea how could I solve this issue?
static void Main(string []args)
{
if (Environment.UserInteractive)
{
// Start the WCf service
var host = new ServiceHost(typeof(WcfService));
host.Open();
//Launch the Kiosk Agent which connects to the WCF
bool ret = ProcessAsUser.Launch("C:\\Program Files (x86)\\KIOSK\\KioskAgent.exe");
WinService kiosk = new WinService(args);
// some checks and a welcome message is sent to the user.
kiosk.MyStart(args);
//...
//...
}
}
Edit: to clarify a bit more, inside kiosk.MyStart method is where I try to execute the callback to show a welcome message, but the callback is still NULL.
As a result I assume that the client was not properly started for any reason and I launch it once again...
if (WcfService.Callback != null)
WcfService.Callback.UIMessageOnCallback(UIMessage);
else
ProcessAsUser.Launch("C:\\Program Files (x86)\\KIOSK\\KioskAgent.exe");
Add a try catch block over the callback method, if the client not reachable it falls in the catch you can unsubscribe it. Is also good practice send a keepalive message to your client, to check if it available.
private void InformClient(ClientInfo clientInfo)
{
var subscribers = this._subscriberRepository.GetAll();
foreach (var subscriber in subscribers)
{
try
{
if (subscriber.Callback.FireInformClient(clientInfo));
{
//If subscriber not reachable, unsubscribe it
this._subscriberRepository.Unsubscribe(subscriber.ClientId);
}
}
catch (Exception exception)
{
//If subscriber not reachable, unsubscribe it
this._subscriberRepository.Unsubscribe(subscriber.ClientId);
Log.Error(nameof(InformClient), exception);
}
}
}
IClientCallback
public interface IClientCallback
{
[OperationContract]
bool FireInformClient(ClientInfo clientInfo);
}
If you have more subscribers for example a terminal, server create a subscriberRepository to manage all subscribers.
var callback = OperationContext.Current.GetCallbackChannel<IClientCallback>();
if (this._subscriberRepository.Subscribe(clientId, callback))
{
return true;
}

What is an elegant way of halting consumption of messages gracefully in the C# client for RabbitMQ?

I am setting up a standard standalone thread listening to RabbitMQ in C#. Suppose the method for listening in the thread looks like this:
public void Listen()
{
using (var channel = connection.CreateModel())
{
var consumer = SetupQueues(channel);
while (true)
{
var ea = consumer.Queue.Dequeue(); // blocking call
handler.HandleMessage(channel, ea);
}
}
}
What is an elegant way of halting consumption of messages gracefully in the C# client for RabbitMQ? Keep in mind I have found nothing of use in the RabbitMQ examples/docs or these SO questions:
How to stop consuming message from selective queue - RabbitMQ
How to pause and resume consumption gracefully in rabbitmq, pika python
What is the best way to safely end a java application with running RabbitMQ consumers
The issue here is consumer.Queue.Dequeue() is a blocking call. I have tried these options:
Calling channel.BasicCancel(string tag). This causes a System.IO.EndOfStreamException in the blocking call. I do not want to use this exception as part of the control flow for obvious reasons.
Calling consumer.Queue.Dequeue(int millisecondsTimeout, out T result) and checking a flag in between loop iterations. This can work but seems hacky.
I want to let the thread exit gracefully and clean up any unmanaged resources I might have, so no thread aborting, etc.
Any help is appreciated. Thanks
The DeQueue with the timeout & flag is the way to do it. It's a very common pattern, and is why many blocking calls are provided with timeout-enabled versions.
Alternately, throwing a (known) exception isn't necessarily a bad thing for control flow. Gracefully shutting down could mean actually trapping the exception, commenting "this is thrown when requesting the channel shuts down", and then returning cleanly. This is how part of TPL works with the CancellationToken.
The blocking methods are not property event-driven.
I don't why they suggest to use consumer.Queue.Dequeue();
Anyway, I usually don't use consumer.Queue.Dequeue();
I extend the default consumer, in this way:
class MyConsumer : DefaultBasicConsumer {
public MyConsumer(IModel model):base(model)
{
}
public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, IBasicProperties properties, byte[] body) {
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] Received {0}", message);
}
}
class Program
{
static void Main(string[] args)
{
var factory = new ConnectionFactory() { Uri = "amqp://aa:bbb#lemur.cloudamqp.com/xxx" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare("hello", false, false, false, null);
var consumer = new MyConsumer(channel);
String tag = channel.BasicConsume("hello", true, consumer);
Console.WriteLine(" [*] Waiting for messages." +
" any key to exit");
Console.ReadLine();
channel.BasicCancel(tag);
/*while (true)
{
/////// DON'T USE THIS
var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] Received {0}", message);
}*/
}
}
}
}
In this way you don't have a blocking method, and you can release all the resources correctly.
EDIT
I think using ctrl+C to break a program is always wrong.

Multithreaded loop with rabbitMQ consumer and WebApi call - WebApi response never returned

I've got a multithreaded Windows service which is consuming messages off a Rabbit queue and sending emails based on the content of the messages.
When the rabbit client is initialized at startup, it limits the Threadpool threads with a Min and Max value.
For each message taken off the queue the service is sending an HTTP request to a web api service using HttpClient GetAsync method to retrieve email address.
The problem is that the request goes off to the data service, but the response never comes back. The windows service keeps consuming messages off the queue and hangs after a while (probably runs of of free threads) - it's waiting for any of the calls to web api to complete which they never do.
I was able to resolve the problem using a Semaphore class for the Rabbit loop rather than trying to limit the Threadpool directly, however, I'd like to know why the service got into this state in the first place. Is that to do with the GetAsync call? Is it perhaps freeing up the thread for the duration of the request, so that the main loop can steal it for a next request?
Any ideas?
The original loop:
while (!_stopped)
{
if (_paused) continue;
try
{
using (var messageBusReceiver = _rabbitQueueClient.ConfigureMessageBusReceiver())
{
using (_consumer = messageBusReceiver.Listen<PublishableItem>())
{
while (!_stopped)
{
if (_paused) continue;
_consumer.Consume(callback, consumeSynchronously: false);
_communicationErrorCount = 0;
}
}
}
}
The Consume method is eventually doing this:
_threadPoolProvider.QueueUserWorkItem(o =>
consumeMessage(callback, eventArgs, o), message);
The callback begins with the following lines - the null checking line is never reached:
var foo = _fooService.GetFoo(messageInfo.FooId);
if (foo == null)
{
throw new FooNotFoundException(
String.Format(CultureInfo.InvariantCulture, "Foo was not found for FooId of {0}", messageInfo.FooId));
}
The client method:
public Foo GetFoo(Guid id)
{
var path = getPathWithQueryStringAndDebug("getfoo", "id", id.ToString());
var response = _client.GetAsync(path).Result;
return processResponse<FooDto>(response);
}

What is better approach to listen in MultiThreading Service?

I am relatively new both to MSMQ and Threading in .NET. I have to create a service which listen in different threads, via TCP and SNMP, several network Devices and all this stuff run in dedicated threads, but here also is required to listen on MSMQ Queue from another applications.
I am analyzing another similar projects and there is used next logic:
private void MSMQRetrievalProc()
{
try
{
Message mes;
WaitHandle[] handles = new WaitHandle[1] { exitEvent };
while (!exitEvent.WaitOne(0, false))
{
try
{
mes = MyQueue.Receive(new TimeSpan(0, 0, 1));
HandleMessage(mes);
}
catch (MessageQueueException)
{
}
}
}
catch (Exception Ex)
{
//Handle Ex
}
}
MSMQRetrievalThread = new Thread(MSMQRetrievalProc);
MSMQRetrievalThread.Start();
But in another service (message dispatcher) I used asynchronous messages' reading based on MSDN Example:
public RootClass() //constructor of Main Class
{
MyQ = CreateQ(#".\Private$\MyQ"); //Get or create MSMQ Queue
// Add an event handler for the ReceiveCompleted event.
MyQ.ReceiveCompleted += new
ReceiveCompletedEventHandler(MsgReceiveCompleted);
// Begin the asynchronous receive operation.
MyQ.BeginReceive();
}
private void MsgReceiveCompleted(Object source, ReceiveCompletedEventArgs asyncResult)
{
try
{
// Connect to the queue.
MessageQueue mq = (MessageQueue)source;
// End the asynchronous Receive operation.
Message m = mq.EndReceive(asyncResult.AsyncResult);
// Process received message
// Restart the asynchronous Receive operation.
mq.BeginReceive();
}
catch (MessageQueueException Ex)
{
// Handle sources of MessageQueueException.
}
return;
}
Does asynchronous handling suppose that every message will be handled in other than main thread?
Could and need this (2nd) approach be put in separate thread?
Please advice better approach or some simple alternatives.
Messages arrival in Queue doesn't have some rule-defined behavior. It may be that for long time no nay message will arrive or in one second there my arrive many (up to 10 or even more) messages. Based on actions defined in some message it will need to delete/change some objects having running threads.
I highly recommend using WCF for MSMQ.
http://msdn.microsoft.com/en-us/library/ms789048.aspx
This allows you to both asynchronous handle the incoming calls using the WCF threading model which allows for throttling, capping, retries, etc...

Invalid Operation Exception In Calling Async Method from inside a Parallel.ForEach Loop

I have inherited a windows service that processes a large number of e-mails in a queue. Sounds simple, Grab queue, send e-mail, if SmtpClient.SendAsync does not return an error from the call back then flag the e-mail in the DB as being sent.. I am using a Semaphore to waitone on the thread so multiple calls can be made to the Async Send method of the SMTP Client. This is the only way I can get the status and per Microsoft docs it has to finish the operation before another call can be made async. So now for the fun part. I decided to use a Parallel.ForEach to get he queue like so. This method is called in the Windows Service OnStart. Please note I have tried calling this method on a separate Thread and get the same results.
I am thinking that either A, I am missing something obvious, due to my lack of knowledge on threading, or something is flat bugged. Most likely A.
private static void ProcessEmailQueue()
{
List<EmailQueue> emailQueue =
_repository.Select<EmailQueue>().Where(x => x.EmailStatuses.EmailStatus == "Pending").ToList();
Parallel.ForEach(emailQueue, message =>
{
_smtpMail.FromAddress = message.FromAddress;
_smtpMail.ToAddress = message.ToAddress;
_smtpMail.Subject = message.Subject;
_smtpMail.SendAsHtml = message.IsHtml > 0;
_smtpMail.MessageBody = message.MessageBody;
_smtpMail.UserToken = message.EmailQueueID;
bool sendStatus = _smtpMail.SendMessage();
// THIS BLOWS UP with InvalidOperation Exception
});
}
Here is the SMTP Method being called from withing the loop.
public bool SendMessage()
{
mailSendSemaphore = new Semaphore(0, 10); // This is defined as private static Semaphore mailSendSemaphore;
try
{
var fromAddress = new MailAddress(FromAddress);
var toAddress = new MailAddress(ToAddress);
using (var mailMessage = new MailMessage(fromAddress, toAddress))
{
mailMessage.Subject = Subject;
mailMessage.IsBodyHtml = SendAsHtml;
mailMessage.Body = MessageBody;
Envelope = mailMessage;
smtp.SendCompleted += smtp_SendCompleted;
smtp.SendAsync(mailMessage, UserToken);
mailSendSemaphore.WaitOne();
return _mailSent;
}
}
catch (Exception exception)
{
_logger.Error(exception);
return _mailSent;
}
}
CALLBACK For Smtp Send
private void smtp_SendCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Cancelled)
{
}
if (e.Error != null)
{
}
else
{
_mailSent = true;
}
mailSendSemaphore.Release(2);
}
Here is the Exception, took a few to get it for some odd reason.
System.InvalidOperationException was unhandled by user code
Message=An asynchronous call is already in progress. It must be completed or canceled before you can call this method.
Source=System
StackTrace:
at System.Net.Mail.SmtpClient.SendAsync(MailMessage message, Object userToken)
at DFW.Infrastructure.Communications.SmtpMail.SendMessage() in SmtpMail.cs:line 71
at EmaiProcessorService.EmailQueueService.b_0(EmailQueue message) in Service1.cs:line 57
at System.Threading.Tasks.Parallel.<>c_DisplayClass2d2.<ForEachWorker>b__23(Int32 i)
at System.Threading.Tasks.Parallel.<>c__DisplayClassf1.b__c()
InnerException:
Seems my waitone is getting obliterated by System.Threading.Tasks.Parallel
Okay, now that we've got the error text, it seems fairly clear:
Message=An asynchronous call is already in progress. It must be completed or canceled before you can call this method.
This concurs with the documentation:
Two simple options:
Create a fixed number of clients, and a queue of messages to send. Make each client take a message from the queue each time it finishes, until the queue is empty. BlockingCollection<T> is good for this.
Create a new SmtpClient per message. This could cause you to effectively launch a DOS attack on your SMTP server, which isn't ideal.
To be honest, it's not really clear why you're using SendAsync when you're then just waiting for the message to be sent anyway...
I'm not clear on why you're using a Semaphore here, but you're almost certainly using it incorrectly. You're creating a new semaphore instance for each call to SendMessage. Also, you're calling WaitOne on it once, and then calling Release(2), so eventually you'll have more releases than acquires. That's probably what causes your InvalidOperationException.
It doesn't do you any good to parallelize processing of the email queue, since you can only send one message at a time. And trying to do it asynchronously inside of the Parallel.Foreach is just more needless complication.
You're better off using something like ThreadPool.QueueUserWorkItem, and having a simple loop that sends one message at a time.
List<EmailQueue> emailQueue =
_repository.Select<EmailQueue>().Where(x => x.EmailStatuses.EmailStatus == "Pending").ToList();
ThreadPool.QueueUserWorkItem(ProcessEmailQueue, emailQueue);
void ProcessEmailQueue(object state)
{
List<EmailQueue> emailQueue = (List<EmailQueue>)state;
foreach (var message in EmailQueue)
{
// Format and send message here.
}
}
Alternatively, you can do the same thing with a Task. The point is that you just need a single thread to process the queue sequentially. Since you can't send more than one message at a time, Parallel.ForEach doesn't do you any good.
EDIT:
If you need to do multiple sends at a time, you can probably modify your original code. First, initialize the semaphore at class scope:
private static Semaphore mailSendSemaphore = new Semaphore(10, 10);
Then, in your SendMessage method:
bool SendMessage()
{
// acquire semaphore. This will block until there's a slot available.
mailSendSemaphore.WaitOne();
try
{
// do all your processing here, including sending the message.
// use Send rather than SendAsync
}
finally
{
mailSendSemaphore.Release();
}
}
There's no need to use SendAsync.

Categories

Resources