Perform asynchronous operations that may be performed on the IIS website, even if the application pool is being processed

Some of the actions that my website users take are sending emails. The code that sends emails may be blocked for a while, so I want to do this from the HTTP request handler thread.

I am currently using something like:

ThreadPool.QueueUserWorkItem(o => { try { email.Send(); } catch (Exception ex) { _log.Error("Error sending email", ex); } }); 

For the most part this works. However, the website operates in an environment in which the application pool can be recycled.

From time to time, I do not receive an email to be sent, and I suspect that this work item in the threadpool queue is deleted during the disposal of the application pool.

How can I perform such ansync operation and guarantee that it will be completed in that case?

+8
asynchronous iis threadpool
source share
2 answers

If the application is running in integrated mode, you can register the mail dispatcher service in the host environment. The host will notify your service before processing is completed. It will do 2 calls to your IRegisteredObject.Stop implementation. At the first call, the host gives you the opportunity to finish the job. If the timeout is reached and your service has not left the host, another call is made, but this time only to notify that the processing will be carried out with or without the consent of the service.

This is an example (not tested) of how you can implement the Stop () method:

 public class MailDispatchService : IRegisteredObject { private AutoResetEvent _processQueueEvt = new AutoResetEvent(); private ConcurrentQueue<MailMessage> _queue = new ConcurrentQueue<MailMessage>(); private Thread _dispatcherThread; private volatile bool _enabled = true; #region Implementation of IRegisteredObject public void Stop(bool immediate) { if (_dispatcherThread != null && _dispatcherThread.IsAlive) { // is not a immediate stop, we can wait for the queue to empty if (!immediate) { // stop to accept new requests... _enabled = false; // awake dispatcher thread, so it can quit if the queue is empty _processQueueEvt.Set(); // and wait for a while but not forever. _dispatcherThread.Join(TimeSpan.FromSeconds(30)); } else { // host env will recycle now, nothing to do _dispatcherThread.Abort(); } } // remove the service from host HostingEnvironment.UnregisterObject(this); } #endregion public void Start() { _dispatcherThread = new Thread(ProcessQueue); _dispatcherThread.Start(); } private void ProcessQueue() { while (_enabled) { _processQueueEvt.WaitOne(); MailMessage message; while (_queue.TryDequeue(out message)) { /* send mail ...*/} } } public void DispatchEmail(MailMessage message) { if (!_enabled) throw new Exception("...."); _queue.Enqueue(message); _processQueueEvt.Set(); } } 

Start the service and register it on the host.

 var mailService = new MailDispatchService(); System.Web.Hosting.HostingEnvironment.RegisterObject(mailService); mailService.Start(); var message = new MailMessage(); mailService.DispatchEmail(message); 
+8
source share

The best option here is to use a constant message queue or service bus. Your application sends an email request to the queue, the queue handler (which may be your web application) reads the queue and processes the message. If something dies, the persistence angle is triggered - messages hang around until they are processed.

+2
source share

All Articles