Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How async / await can help in ASP.Net application?

Using async / await in action method of MVC controller can scale up the web application because at await the request thread of Asp.Net thread pool is freed up so that it can process other requests in the IIS queue for this worker process. This means that if we limit the Queue Length of worker process to 10, and send 50 - 100 requests to async action, the IIS should not return HTTP 503 error as there will always be a free thread from Asp.Net Thread Pool to server the incoming requests.

I have a WebApi that does the calculation as given below:

public class ValuesController : ApiController
{
    public int GetSum(int x, int y, int timeDelay = 1)
    {
        Thread.Sleep(timeDelay*1000);
        int result = x + y;
        return result;
    }
}

This action method just delays for given number of seconds before returning the sum result to the calling code. Very basic web api just to mimic the long running code.

Next is the MVC async action that awaits for the result:

public class ThreadStarvationController : Controller
{
    public async Task<ActionResult> CallGetSumWithDelayAsync(int num1 = 5, int num2 = 8, int timeDelay = 60)
    {
        int callingThreadId = Thread.CurrentThread.ManagedThreadId;
        ThreadStarvationModel model = new ThreadStarvationModel();

        string url = "http://localhost:8111/api/values/GetSum?x=" + num1 + "&y=" + num2 + "&timeDelay=" + timeDelay;

        using (HttpClient client = new HttpClient())
        {

            // here still using the same request thread...
            // following line will force await to free up the request thread and wait asynchronouly               //for the response.
            model.ResponseFromWebService = await client.GetStringAsync(url);

            // here we can be on the same request thread or anyother thread... more likely on //another other thread than 
            // request thread.
        }

        int returningThreadId = Thread.CurrentThread.ManagedThreadId;

        model.CallingThreadId = callingThreadId;
        model.ReturningThreadId = returningThreadId;

        return this.View(model);
    }
}

The WebApi and MVC are hosted on IIS. The MVC website is limited to only 10 requests in queue.

When the client calls MVC async method after 15 or 20 requests the IIS sends HTTP 503 error which means that the IIS Queue is full with requests.

Here is the console application code that calls the MVC async method. It schedules 30 tasks and executed them in parallel.

          List<Task> taskList = new List<Task>();
        for (int x = 0; x < 30; x++)
        {
            string url = "http://localhost:8333/ThreadStarvation/CallGetSumWithDelayAsync?num1=" + x + "&num2=" + x + "&timeDelay=1";

            Task stringDataTask = new Task(() =>
            {
                using (HttpClient webClient = new HttpClient())
                {
                    string data = webClient.GetStringAsync(url).Result;
                    Console.WriteLine("{0}", data);
                }
            });


            taskList.Add(stringDataTask);
        }

        DateTime startTime = DateTime.Now;

        Parallel.ForEach(taskList, item => item.Start());

        Console.WriteLine("================== START {0} ===========================", startTime);

        Task.WaitAll(taskList.ToArray());
        DateTime endTime = DateTime.Now;
        Console.WriteLine("================= THE END {0} ============================", endTime);

When this runs, after 20 or so requests I get HTTP 503 error message.

If I use synchronous MVC action, the result is still the same. I know that with async / await different threads are used before and after await.

All I want to prove is that using async / await will scale up the web application.

like image 550
Yawar Murtaza Avatar asked Mar 18 '23 10:03

Yawar Murtaza


1 Answers

I think you are confusing pool queues. There are 5 places where ASP.NET requests can become queued on an IIS server.

  1. Application pool queue
  2. IIS worker process
  3. CLR threadpool queue
  4. Integrated mode global queue
  5. Classic mode application queue

The Queue Length you are setting to 10 is HTTP.SYS: Application pool queue.

When you use async/awat you are using ASP.NET: CLR threadpool queue.

That is the reason why you get 503 error even with async/await.

On the other hand, here there is a wonderful article about scalling web app with async/await that can help you.

[EDIT] I had just found this article about request queuing that can help too.

like image 129
jlvaquero Avatar answered Mar 20 '23 18:03

jlvaquero