Concurrent Requests In ASP.NET MVC

Update:  Now, ASP.NET MVC 3 support Session-less Controller.

    Introduction:

          According to Levi Broderick(one of the awesome ASP.NET MVC team player), concurrent requests will/may be supported in ASP.NET MVC 3 . Also Rick Anderson(another genius ASP.NET MVC team player) had written a very good article that shows how to use this future functionality here. But before using this future functionality, it is really very important to understand that what are the advantages and disadvantages of using concurrent requests. So in this article i will try to explain that what are the limitations and advantages of using concurrent requests (also known as parallelization). But before I start this article, I am giving special thanks to both Levi Broderick and Rick Anderson for their fantastic tips.

   Description:

          Concurrent requests simply means that responding multiple requests by a web server simultaneously. In simple words you can say that a server is responding multiple requests without waiting for a request to complete. ASP.NET doing the same task using CLR Thread Pool. So what new features we can get if we use concurrent request in ASP.NET MVC? The simple answer is that we can handle concurrent requests for the same user because there are some limitation if we use concurrent requests for the same user. So in this article I will be referring concurrent requests as concurrent requests for single user instead of different users.

          One advantage of using concurrent requests is the ability to get response from different long running request quickly. For example, you may need to get some data from a long running web service and at the same time you need to run a complex query to get some data. Without concurrent requests you need to wait for remote web service to respond before you start to send a request to run a complex query. But with concurrent requests both of your requests will run in parallel, which will dramatically improves the performance.

          Another advantage of using concurrent requests is to load your page quickly by sending only the necessary html/data to user on initial request and then using ajax and concurrent requests, get the remaining html/data. Gmail is the very good example which only send the basic data to the user on initial request and then using ajax and concurrent requests, get the remaining or extra data which makes their application to load quickly and respond on subsequent requests fastly. ASP.NET community page is the another example.

          So what are the limitations now? Before I start discussing the limitations, let's create a new ASP.NET MVC Application first. Then open the global.asax.cs file and add Application_BeginRequest event in this file and put a break point in this event.
         

         protected void Application_BeginRequest()
         {

         }

          Next, open the Site.Master file and reference the jquery file,
         
         <script src="../../Scripts/jquery-1.4.1.js" type="text/javascript"></script>

          Next, add a new controller ConcurrentRequestController and add some code inside Index action,
     
          public class ConcurrentRequestController : Controller
          {
              public ActionResult Index()
              {
                  Thread.Sleep(5000);
                  return Content(DateTime.Now.ToShortDateString())
              }

          }

      Also put a break point inside this method. This action method will be used to handle concurrent requests.

      Next, add the following lines in your Home Index view,
     
         <input type="button" id="btn" value="Send Concurrent Requests" onclick="Call()" />
         <div id="div1">
        
         </div>
         <script type="text/javascript">
            function Call() {
               for (var i = 0; i < 7; i++) {
                  $.get('/ConcurrentRequest', function (data) {
                     $("#div1").html($("#div1").html() + "<br>" + data)
                  });
              }
            }
         </script>

          Now let's see the limitations,

   Browser Limitations:

          The very important limitations we need to aware is the browser limitations for sending concurrent requests. Different browser allows different number of concurrent request to a single server. You can find the detail of how many number of concurrent request allowed by browser for a single server in this post.

           Let's check this limitation. I am using Firefox 3.5.10 and IE 8.0 browser to test this scenario. Now run the above application and click the "Send Concurrent Requests" button. This will send 7 concurrent request to the same server with same URL. What will happen in Firefox is that our Application_BeginRequest event will trigger once and before it trigger second time it will wait for the first request to complete. This simply means that Firefox only send one request to the same URL at a time and queue all of remaining requests and if first request completes, it will send the second one and so on.

            Now what happens when you click the "Send Concurrent Requests" button in IE 8. In dial up connection, Application_BeginRequest event will trigger two times before the previous requests completes, while in broadband connection this will trigger 6 times before the previous requests completes. This means that IE send 2 or 6 request to a single web server regardless of the URL while Firefox only send one request to a single web server at a time for same URL. For making both browser behaves same, just append a query string in your code. 

         <script type="text/javascript">
         function Call() {
            for (var i = 0; i < 7; i++) {
               $.get('/ConcurrentRequest?temp='+i, function (data) {
                   $("#div1").html($("#div1").html() + "<br>" + data)
               });
            }
         }
         </script>

             This time when you run your application in Firefox it will behaves same way as IE behaves, because this time URLs pointing to the requests are different( we just append a query string).

             So the net result is that when you are using concurrent requests in ASP.NET be aware about these browser limitations that browser will send a limited number of requests to same server at the same time.

 

     Session Limitations:

              This limitation is independent from browser limitations. This is the limitation which is addressed by ASP.NET MVC in ASP.NET MVC futures found at codeplex known as Session Less MVC Controller. For understanding this limitation, just add a session inside your Index action of HomeController.  

         public class HomeController : Controller
         {
            public ActionResult Index()
            {
               ViewData["Message"] = "Welcome to ASP.NET MVC!";
               Session["temp"] = "Temporary Session";
               return View();
            }
         }

 

               Then put a break point inside your ConcurrentRequestController's Index action and run your application again and click the "Send Concurrent Requests" button. What you will find is that break point will be hit in your Application_BeginRequest event six times(because of browser limitation) and only one break point will be hit in your ConcurrentRequestControlle's Index action method at same time. When the first request completes, break point will be hit second time in ConcurrentRequestControlle's Index action and so on. So what is going on here? The answer is that when ASP.NET see two or more requests from the same user( by checking their SessionID) and session is marked with both Read and Write, then it will execute the first request and queue all other requests such that only one request will execute from the same user at the same time. So we can say that these requests are serialized.

                To make your requests execute concurrently, you need to either mark your session as disabled or read only. You can also use the new feature of ASP.NET MVC, session less controller, as described previously in this article. You can also use my trick to remove the session only for the requests for ConcurrentRequestController actions method. In this way Session will work for all controller actions except ConcurrentRequestController actions. Just put this code inside global.asax.cs and see.

         protected void Application_BeginRequest()
         {
            if (Request.Url.AbsoluteUri.ToLower().Contains("concurrentrequest") && Request.Cookies["ASP.NET_SessionId"] != null)
            {
                Request.Cookies.Remove("ASP.NET_SessionId");
            }
         }

                 Just run your application with and without above code, When you not use the above code, you will find that your action method break point will hit once and before the break point hit second time it will wait for the first request to complete, this means that requests are serialized. But when you use the above code,  your action method break point will hit continuously without waiting one request to complete, this means that request are parallelized.

 

      Thread Limitations:

              More threads will always slow the performance of an application. According to Jeffrey Richter, "If all we cared about was raw performance, then the optimum number of threads to have on any machine is identical to the number of CPUs on that machine. So a machine with one CPU would have only one thread, a machine with two CPUs would have two threads, and so on. The reason is obvious: If you have more threads than CPUs, then context switching is introduced and performance deteriorates.". That's why ASP.NET handles its requests using CLR Thread Pool.

               CLR Thread Pool has fixed size and the default size is 250 worker threads per available processor. So what happens when 100 users request the ConcurrentRequestController action at same time? Instead of 100 requests, ASP.NET treat these requests as 600 requests. So you need to be very careful and make some good decisions when you are using concurrent requests.

                Also you need to aware about http.sys threads and IIS threads. http.sys thread forward the request to IIS thread and IIS thread forward the request to ASP.NET thread. This is again another limitation you need to be aware that concurrent requests spawn more threads and more threads resuls in more context switching which affect your application performance. You can find good explanation at here.

 

    Summary:

          In this article I try to explain the concurrent requests in ASP.NET. I also try to explain the advantages and limitations of concurrent requests with example. Hopefully, this helps you to make the decision whether concurrent request is feasible for you. Again I like to  thanks to both Levi Broderick and Rick Anderson. Hopefully, you will enjoy this article.

9 Comments

  • Great article Imran...
    Keep up the good work you do!

  • Imran,

    Beautiful Post:
    You can also add Server side Web service calls limitations! , by default only 2 requests are permitted under System.Net.

    Another point that we've noted is that, when we use "Write Session" in the current page, then Asp.net won't initiate any Async calls! all the calls will be Sync.

    Thanks,

    Gopi

  • This is very nice!
    I have an appplication where I first load a page, and then multiple graphs are loaded asynchronously by ajax cals using jQuery. Each of these calls seems now to be done one at a time, which makes it really slow. Great feature, highly appreciated. Thanks for making the light :)

  • if (Request.Url.AbsoluteUri.ToLower().Contains("concurrentrequest")
    can be rewritten as
    if (Request.Url.AbsoluteUri.IndexOf("concurrentrequest", StringComparison.OrdinalIgnoreCase) >= 0

  • great post vah

  • With the Application_BeginRequest solution each response returns a new session cookie. You are just pretending that a session does not yet exist and IIS creates a new one (without blocking). What is the potential scalability issues with potentially hundreds of sessions with nothing in them.

    Is it possible to use the MVC 3 style solution of ControllerSessionState(ControllerSessionState.ReadOnly)] with a MVC 1 site?

  • To attempt to deal with scalability I added something similar to the following to the MVC 1 project global.asax to destroy the session straight away

    protected void Application_PostRequestHandlerExecute()
    {
    protected void Application_BeginRequest()
    {
    if (Request.Url.AbsoluteUri.ToLower().Contains("concurrentrequest") && Response.Cookies["ASP.NET_SessionId"] != null)
    {
    Response.Cookies.Remove("ASP.NET_SessionId");
    Session.Timeout = 1;
    Session.Abandon();
    }
    }

    }

  • Hi Imran, I understand the sessionless controller behavior as either its serialize or we can made this paralleralize,but this is not working in my case,
    In both of the situations my break point first hit the application_beginrequest and then my controller's action.

    Here is my Code.
    public ActionResult Content()
    {
    Session["myid"] = "Vishal";
    //Thread.Sleep(6000);
    return Content(DateTime.Now.ToShortDateString());
    }

    protected void Application_BeginRequest()
    {
    //if (Request.Cookies["ASP.NET_SessionId"] != null)
    //{
    // Request.Cookies.Remove("ASP.NET_SessionId");
    //}
    }

  • @Ramesh, read this article thoroughly.

Comments have been disabled for this content.