I’m thinking out loud here about handling asynchronous requests in ASP.NET. I have an application that is running some pretty hairy and fairly lengthy (read about 2-3 minutes) reports which can be fired simultaneously by a number of users. What I’m after here is basically to start a request, pass it off to an external process (or machine) for processing, have the Web app check for a response, notify the user of progress and then eventually return a result back. All this without locking up an ASP. Net thread.
This is actually a fairly common scenario, so while I’m building this I want to extend my page framework to support this in a reusable fashion eventually.
ASP. Net is fairly forgiving for managing simultaneous request in most cases, but when you run a report like the above there’s bound to be a problem in any kind of a load scenario should it ever arise. In addition, in this case this happens to be a semi public link there’s always the chance that users will click multiple times by going back and resubmitting reports. In short, there’s a potential scalability problem.
A long while back I created a class to handle this sort of scenario for my FoxPro based West Wind Web Connection framework and I wrote about this process in an article. In this article I discuss a database driven approach to queuing requests and processing them with an external application that can live either locally or on another machine. This process has worked very well for me and a number of customers who've used in a number of fairly large applications.
As I sat down to tackle this problem in ASP. Net I wanted to review what options are available. ASP. Net actually supports mechanisms for running requests asynchronously through Async Handlers. Both plain HTTP handlers and the Page object for the WebForms engine support asynchronous processing. Unfortunately for load based scenarios this options don’t provide any relief as the threads used for these async handlers come from the same pool of threads that ASP. Net uses to process requests in the first place. So while you can ‘offload’ processing to threads outside of the ASP. Net thread queue, you’re still using the same pool of threads. In other words this doesn’t even buy any more threads, nor does it really address the scenario where you might want to dump processing off onto another machine because the local CPU(s) can't handle the processing load.
You can however fire off new threads from these Async HTTP handlers and manage these threads on your own and notify ASP. Net when your thread is done processing. This is a little better but this process is not pleasant to work with if you want to build a reusable scenario, and it doesn’t address the issue of offloading either.
Given that pure threading isn’t going to solve this problem the next option would be some sort of queuing mechanism. I’ve worked with MSMQ on a number of occasions and while it works in some scenarios I’m not sure if it is a good fit in this one. Queuing works great for pure ‘Call and forget’ interfaces where you submit something to a queue and the queue entries get picked up by another process at a later time. For this sort of thing MSMQ is easy and very efficient. But if you now need to return a response of some sort back to the caller (usually a polling caller) the process gets very messy. I’ve done this in the past with a Response queue which is then queried by the caller for a result. It works but it requires extra setup, a fair amount of code and it can easily introduce transactional problems if events fail to send responses which further complicates this process.
Another alternative for responses are Queued Events which can give some sort of indication that a process has completed, but given a stateless client like a Web Request, this sort of thing is really useless. In addition this works through COM+, which means you’re getting into COM interop – something I would much rather avoid if I can at all help it in a .NET application.
In fact, I’ve been wondering about MSMQ as a two way communication mechanism ( Request – Response asynchronously) and my conclusion is that while you can make MSMQ work for this with multiple queues (input and output) the process to do this is neither clean nor efficient. MSMQ doesn’t like to retrieve messages in random, non-queued order and making it work in a scenario where you need to send a response back ends up being quite a bit of work.
I haven’t worked with MSMQ that extensively, I’m curious if others share this point of view or can point at other approaches that I may have missed here…
One more thing that really sucks about MSMQ is the fact that messages are limited to 4megs. The first application I built with MSMQ together with Markus for a freight company dealt with forwarding Web Requests from one Web server to another over a WAN that was unreliable and often not available. These requests basically allowed queing incoming Web requests in the case where the connection to the remote was down. When the connection would come back up the other end would then pick up the queued requests and process them. Problem there is that request responses could be any size and we pretty quickly hit the 4 meg barrier. The code that had to split messages (and check whether a message was a split message) was a pure nightmare and involved having to package every single message into a header wrapper…
I wonder why, oh why there’s this 4 meg limit? I suspect it has to do with the optimization of the messages and large messages clogging the performance, but still if 4 megs are exceeded in messages MSMQ becomes a drag immediately.
Soooo… this makes me think that for this sort of Asynchronous processing with response requirements a Database message system is actually a much better approach. For one there’s no 4 meg limit. And with a database you have much more control over the actual inbound queue items including the ability to provide feedback that both the client and server can read and use to communicate with each other. For example, there could be status messages that are set by the processing server, that get routed back to the client that can get displayed on the next page refresh for the user. There’s also the ability to quickly search requests and possibly keep them around for review or possible later playback – another thing that would be difficult to do with MSMQ.
The idea is that there is one 'event' in the database that is identified by ID that both server and client can look at. The Client submits and the server picks up the first event. But the server can then write back into this 'pending' event and update information and return a response of some sort. It works both for the true Async scenario as well the Async request-response scenario, that is not such a good fit for MSMQ.
The downsides would be that it's a non-standard component and you need a database (SQL Server most likely to make sure the transacting works properly). And you lose some of the transaction features of the Queue, although that would be relatively easy to handle with completion marks.
I haven’t written any code for this component yet, but I am about to do this and it’ll likely be similar to the old Fox code I have. I’ve used this mechanism in the past and it’s worked reliably. The main difference likely will be though that the data stored will be serialized (much like the Messaging classes in the CLR) instead of only dealing with Text or ‘raw data’ (Text, Html, XML, or binary).
But I’m still wondering if there’s not something in the system that provides this sort of functionality more generically…
Any thoughts?
Other Posts you might also like