2

I have a Web API project whose controller in turn makes web requests to a third party REST interface. My controller CRUD functions i have marked as async however the implementation is such that i use HttpWebRequest blocking functions GetRequestStream and GetResponse.

Is this suitable or should i use the Begin/End methods of HttpWebRequest too. This seems like overkill as it would be asyncing an already async operation, or would it allow more concurrent outgoing web requests?

Looking for the best solution for throughput both incoming and outgoing.

user1371314
  • 762
  • 7
  • 24
  • You say you mark those methods `async` but are using blocking methods? Just because a methods is marked `async` does not mean it's asynchronous; in particular, if you do not use `await`, then it's synchronous (check your warning messages). – Stephen Cleary Apr 28 '14 at 11:48
  • Yes the controller is marked async but the implementation uses synchronous HttpWebRequest calls, in that my controller is awaiting Task.Run( sync web request ). So i guess what is happening is that i am getting a lot of async incoming requests who are all then queued waiting on the sync outbound requests? The thing i dont understand though is if i use await HttpClient.SendAsync then somewhere something must be blocking waiting for a response, how is using SendAsync helping my outbound throughput if i have a 4 core box but 100 requests? – user1371314 Apr 29 '14 at 01:24

3 Answers3

1

the controller is marked async but the implementation uses synchronous HttpWebRequest calls, in that my controller is awaiting Task.Run( sync web request )

Think about what is happening in the request. The request comes in and ASP.NET takes a thread pool thread to handle the request. The controller action queues the work to the thread pool (taking up another thread), and then awaits that work, freeing up the original request thread. You haven't gained anything by using await because there's still a thread pool thread blocking on the web request.

For this reason, you should almost never use Task.Run (or any other method that queues work to the thread pool) on ASP.NET.

Is this suitable or should i use the Begin/End methods of HttpWebRequest too. This seems like overkill as it would be asyncing an already async operation

It's not really asynchronous right now; there's still a thread being blocked. I call this "queue blocking work to a thread pool thread and then await it" technique fake asynchrony.

The appropriate fix is to use HttpClient, which was designed for asynchronous use; or, you could use TaskFactory<T>.FromAsync to wrap the Begin/End methods into an awaitable task.

The thing i dont understand though is if i use await HttpClient.SendAsync then somewhere something must be blocking waiting for a response

No, there doesn't have to be something blocking somewhere. As I describe on my blog, in a truly asynchronous scenario (i.e., not fake-asynchrony), there is no thread.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Ok cool so i have changed the code such that my controller awaits on my HttpClient async calls. Finally i have another requirement to use a 3rd party C++ library that clearly does not support async (uses curl) so what is the best approach to that? – user1371314 Apr 30 '14 at 01:07
  • If it's not asynchronous and you can't make it (truly) asynchronous, then just call it synchronously. – Stephen Cleary Apr 30 '14 at 12:36
0

I would recommend you to retain the async methods, and use HttpClient instead of HttpWebRequest, because it supports asynchronous calls as well.

Let's have a look at a pseudo example of a CarsController calling a third party service.

 public class CarsController : ApiController
    {
        [Route("cars/{id}")]
        public async Task<IHttpActionResult> Get(int id)
        {

            //Get a car by its id

            HttpClient client = new HttpClient();

            //Read the content directly
            string result = await client.GetStringAsync("http://www.google.com");

            //process the result returns from the thrid party REST service

            return Ok(result);
        }

}

As you can see, HttpClient supports reading String/ Stream/ ByteArray asynchronously via GetXXXAsync() methods.

If you want to access to the underlying response content, that can be achieved by using the following code:

        [Route("responsecars/{id}")]
        public async Task<IHttpActionResult> GetResponseStream(int id)
        {
            HttpClient client = new HttpClient();
            HttpResponseMessage responseMessage =await client.GetAsync("http://www.google.com", HttpCompletionOption.ResponseContentRead);

            HttpContent content = responseMessage.Content;

            //using one of the ReadAsync methods
            string text =await content.ReadAsStringAsync();

            return Ok(text);

        }

Hope this help.

Toan Nguyen
  • 11,263
  • 5
  • 43
  • 59
  • Yes correct but is it of any benefit. If my controller is already making async calls to the 3rd party interface then is there any additional benefit on making those calls async too or is it in fact adding extra overhead? – user1371314 Apr 28 '14 at 04:02
  • Hi there, there are benefits definitely! because if the calls take a lot of time, the server can free up your current thread to serve other requests. – Toan Nguyen Apr 28 '14 at 04:07
0

Asynchrony on the server is separate and independent from asynchrony on the client.

You can call a synchronous Web API method asynchronously from the client with WebRequest.GetResponseAsync, or you can call an asynchronous Web API method synchronously from the client with WebRequest.GetResponse, or you can have asynchrony on both sides, which is probably the best approach. You don't need to use Begin/End APM-style WebRequest APIs, unless you target .NET 4.0 on the client. Use Task-based async APIs instead.

In either case, a complete HTTP request will be sent to the client when your Web API controller method has fully finished, regardless of whether it is implemented as synchronous or asynchronous (Task-based).

noseratio
  • 59,932
  • 34
  • 208
  • 486