Decidir entre HttpClient y WebClient [cerrado]

Resuelto user3092913 asked hace 10 años • 0 respuestas

Nuestra aplicación web se ejecuta en .NET Framework 4.0. La interfaz de usuario llama a los métodos del controlador mediante llamadas Ajax.

Necesitamos consumir el servicio REST de nuestro proveedor. Estoy evaluando la mejor forma de llamar al servicio REST en .NET 4.0. El servicio REST requiere un esquema de autenticación básico y puede devolver datos tanto en XML como en JSON.

No hay ningún requisito para cargar/descargar datos enormes y no veo nada en el futuro. Eché un vistazo a algunos proyectos de código fuente abierto para consumo REST y no encontré ningún valor en ellos para justificar una dependencia adicional en el proyecto. Empecé a evaluar WebClienty HttpClient. Descargué HttpClient para .NET 4.0 de NuGet .

Busqué diferencias entre WebClienty HttpClienty este sitio mencionó que un solo HttpClient puede manejar llamadas simultáneas y puede reutilizar DNS resuelto, configuración de cookies y autenticación. Todavía tengo que ver los valores prácticos que podemos ganar debido a las diferencias.

Hice una prueba de rendimiento rápida para descubrir cómo funcionan WebClient(llamadas síncronas), HttpClient(síncronas y asincrónicas). Y aquí están los resultados:

Estoy usando la misma HttpClientinstancia para todas las solicitudes (mínimo - máximo).

Sincronización de WebClient: 8 ms - 167 ms
Sincronización de HttpClient: 3 ms - 7228 ms
HttpClient asíncrono: 985 - 10405 ms

Usando un nuevo HttpClientpara cada solicitud (mínimo - máximo):

Sincronización de WebClient: 4 ms - 297 ms
Sincronización de HttpClient: 3 ms - 7953 ms
HttpClient asíncrono: 1027 - 10834 ms

Código

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);
        }
    }
}

Mis preguntas

  1. Las llamadas REST regresan en 3-4 segundos, lo cual es aceptable. Las llamadas al servicio REST se inician en los métodos del controlador que se invocan desde las llamadas Ajax. Para empezar, las llamadas se ejecutan en un hilo diferente y no bloquean la interfaz de usuario. Entonces, ¿puedo seguir con las llamadas sincrónicas?
  2. El código anterior se ejecutó en mi localbox. En una configuración de producción, estarán involucradas las búsquedas de DNS y proxy. ¿ Existe alguna ventaja de usar HttpClientover WebClient?
  3. ¿ Es HttpClientla concurrencia mejor que WebClient? Según los resultados de las pruebas, veo que WebClientlas llamadas sincrónicas funcionan mejor.
  4. ¿ Será HttpClientuna mejor opción de diseño si actualizamos a .NET 4.5? El rendimiento es el factor clave del diseño.
user3092913 avatar Dec 12 '13 04:12 user3092913
Aceptado

HttpClient es la API más nueva y tiene los beneficios de

  • tiene un buen modelo de programación asincrónica
  • Henrik F Nielson, que es básicamente uno de los inventores de HTTP, está trabajando en él y diseñó la API para que le resulte fácil seguir el estándar HTTP, por ejemplo, generando encabezados compatibles con los estándares.
  • está en .NET framework 4.5, por lo que tiene cierto nivel garantizado de soporte para el futuro previsible
  • También tiene la versión xcopy able/portable-framework de la biblioteca si desea usarla en otras plataformas: .NET 4.0, Windows Phone , etc.

Si está escribiendo un servicio web que realiza llamadas REST a otros servicios web, debería utilizar un modelo de programación asincrónica para todas sus llamadas REST, de modo que no se quede sin subprocesos. Probablemente también quieras utilizar el compilador de C# más nuevo que tiene soporte async/await .

Nota: No tiene más rendimiento, AFAIK. Probablemente tenga un rendimiento similar si crea una prueba justa.

Tim Lovell-Smith avatar May 30 '2014 16:05 Tim Lovell-Smith

HttpClientFactory

Es importante evaluar las diferentes formas en que se puede crear un HttpClient y parte de eso es comprender HttpClientFactory.

https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests

Esta no es una respuesta directa, lo sé, pero es mejor comenzar aquí que terminar en new HttpClient(...)todas partes.

Simon_Weaver avatar Feb 05 '2020 00:02 Simon_Weaver

Cuando se trata de aplicaciones ASP.NET, todavía las prefiero WebClientporque HttpClient:

  1. La implementación moderna viene con métodos asincrónicos/en espera basados ​​en tareas.
  2. Tiene una huella de memoria más pequeña y es de 2 a 5 veces más rápido (otras respuestas ya lo mencionan)
  3. Se sugiere " reutilizar una única instancia de HttpClient durante toda la vida útil de su aplicación ". Pero ASP.NET no tiene una "vida útil de la aplicación", solo una vida útil de una solicitud. La guía actual para ASP.NET 5 es usar HttpClientFactory, pero solo se puede usar mediante inyección de dependencia. Algunas personas quieren una solución más sencilla.
  4. Lo más importante es que si está utilizando una instancia única de HttpClient durante la vida útil de la aplicación, como sugiere MS, tiene problemas conocidos . Por ejemplo, el problema del almacenamiento en caché de DNS: HttpClient simplemente ignora el TTL y almacena en caché el DNS "para siempre". Sin embargo, existen soluciones. Si desea obtener más información sobre los problemas y la confusión con HttpClient, simplemente lea este comentario en Microsoft GitHub.
Alex from Jitbit avatar Jun 06 '2020 13:06 Alex from Jitbit