Deciding between HttpClient and WebClient

Our web app is running in .Net Framework 4.0. The UI calls controller methods through ajax calls.

We need to consume REST service from our vendor. I am evaluating the best way to call REST service in .Net 4.0. The REST service requires Basic Authentication Scheme and it
can return data in both XML and JSON. There is no requirement for uploading/downloading huge data and I don’t see anything in future. I took a look at few open source code projects for REST consumption and didn’t find any value in those to justify additional dependency in the project. Started to evaluate WebClient and HttpClient. I downloaded HttpClient for .Net 4.0 from NuGet.

I searched for differences between WebClient and HttpClient and this site mentioned that single HttpClient can handle concurrent calls and it can reuse resolved DNS, cookie config and authentication. I am yet to see practical values that we may gain due to the differences.

I did a quick performance test to find how WebClient (sync calls), HttpClient (sync and async) perform. and here are the results:

Using same HttpClient instance for all the requests (min – max)

WebClient sync: 8 ms – 167 ms
HttpClient sync: 3 ms – 7228 ms
HttpClient async: 985 – 10405 ms

Using a new HttpClient for each request (min – max)

WebClient sync: 4 ms – 297 ms
HttpClient sync: 3 ms – 7953 ms
HttpClient async: 1027 – 10834 ms

Code

public class AHNData
{
    public int i;
    public string str;
}

public class Program
{
    public static HttpClient httpClient = new HttpClient();
    private static readonly string _url = "http://localhost:9000/api/values/";

    public static void Main(string[] args)
    {
       #region "Trace"
       Trace.Listeners.Clear();

       TextWriterTraceListener twtl = new TextWriterTraceListener(
           "C:\\Temp\\REST_Test.txt");
       twtl.Name = "TextLogger";
       twtl.TraceOutputOptions = TraceOptions.ThreadId | TraceOptions.DateTime;

       ConsoleTraceListener ctl = new ConsoleTraceListener(false);
       ctl.TraceOutputOptions = TraceOptions.DateTime;

       Trace.Listeners.Add(twtl);
       Trace.Listeners.Add(ctl);
       Trace.AutoFlush = true;
       #endregion

       int batchSize = 1000;

       ParallelOptions parallelOptions = new ParallelOptions();
       parallelOptions.MaxDegreeOfParallelism = batchSize;

       ServicePointManager.DefaultConnectionLimit = 1000000;

       Parallel.For(0, batchSize, parallelOptions,
           j =>
           {
               Stopwatch sw1 = Stopwatch.StartNew();
               GetDataFromHttpClientAsync<List<AHNData>>(sw1);
           });
       Parallel.For(0, batchSize, parallelOptions,
            j =>
            {
                Stopwatch sw1 = Stopwatch.StartNew();
                GetDataFromHttpClientSync<List<AHNData>>(sw1);
            });
       Parallel.For(0, batchSize, parallelOptions,
            j =>
            {
                using (WebClient client = new WebClient())
                {
                   Stopwatch sw = Stopwatch.StartNew();
                   byte[] arr = client.DownloadData(_url);
                   sw.Stop();

                   Trace.WriteLine("WebClient Sync " + sw.ElapsedMilliseconds);
                }
           });

           Console.Read();
        }

        public static T GetDataFromWebClient<T>()
        {
            using (var webClient = new WebClient())
            {
                webClient.BaseAddress = _url;
                return JsonConvert.DeserializeObject<T>(
                    webClient.DownloadString(_url));
            }
        }

        public static void GetDataFromHttpClientSync<T>(Stopwatch sw)
        {
            HttpClient httpClient = new HttpClient();
            var response = httpClient.GetAsync(_url).Result;
            var obj = JsonConvert.DeserializeObject<T>(
                response.Content.ReadAsStringAsync().Result);
            sw.Stop();

            Trace.WriteLine("HttpClient Sync " + sw.ElapsedMilliseconds);
        }

        public static void GetDataFromHttpClientAsync<T>(Stopwatch sw)
        {
           HttpClient httpClient = new HttpClient();
           var response = httpClient.GetAsync(_url).ContinueWith(
              (a) => {
                 JsonConvert.DeserializeObject<T>(
                    a.Result.Content.ReadAsStringAsync().Result);
                 sw.Stop();
                 Trace.WriteLine("HttpClient Async " + sw.ElapsedMilliseconds);
              }, TaskContinuationOptions.None);
        }
    }
}

My Questions

  1. The REST calls return in 3-4s which is acceptable. Calls to REST
    service are initiated in controller methods which gets invoked from
    ajax calls. To begin with, the calls run in a different thread and
    doesn’t block UI. So, can I just stick with sync calls?
  2. The above code was run in my localbox. In prod setup, DNS and proxy
    lookup will be involved. Is there any advantage of using HttpClient over WebClient?
  3. Is HttpClient concurrency better than WebClient ? From the test results, I see WebClient sync calls perform better.
  4. Will HttpClient be a better design choice if we upgrade to .Net 4.5? Performance is the key design factor.

6 Answers
6

Leave a Comment