Nuestra aplicación web se ejecuta en .Net Framework 4.0. La UI llama a los métodos del controlador a través de llamadas ajax.
Necesitamos consumir el servicio REST de nuestro proveedor. Estoy evaluando la mejor manera 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 requisitos para cargar / descargar grandes datos y no veo nada en el futuro. Eché un vistazo a algunos proyectos de código fuente abierto para el consumo de REST y no encontré ningún valor en ellos para justificar una dependencia adicional en el proyecto. Comenzó a evaluar WebClient
y HttpClient
. Descargué HttpClient para .Net 4.0 de NuGet.
Busqué diferencias entre WebClient
y, HttpClient
y este sitio mencionó que solo HttpClient puede manejar llamadas concurrentes 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 encontrar cómo funcionan WebClient
(llamadas de sincronización), HttpClient
(sincronización y asíncrono). Y aquí están los resultados:
Usar la misma HttpClient
instancia para todas las solicitudes (min - max)
WebClient sync: 8 ms - 167 ms
HttpClient sync: 3 ms - 7228 ms
HttpClient async: 985 - 10405 ms
Usando un nuevo HttpClient
para cada solicitud (min - max)
WebClient sync: 4 ms - 297 ms
HttpClient sync: 3 ms - 7953 ms
HttpClient async: 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
- Las llamadas REST regresan en 3-4 segundos, lo cual es aceptable. Las llamadas al servicio REST se inician en métodos de controlador que se invocan desde llamadas ajax. Para empezar, las llamadas se ejecutan en un hilo diferente y no bloquea la interfaz de usuario. Entonces, ¿puedo seguir con las llamadas de sincronización?
- El código anterior se ejecutó en mi localbox. En la configuración de productos, la búsqueda de DNS y proxy estará involucrada. ¿Hay alguna ventaja de usar
HttpClient
másWebClient
? - ¿Es
HttpClient
mejor la concurrencia queWebClient
? De los resultados de la prueba, veo que lasWebClient
llamadas de sincronización funcionan mejor. - ¿
HttpClient
Será una mejor opción de diseño si actualizamos a .Net 4.5? El rendimiento es el factor clave del diseño.
GetDataFromHttpClientAsync
porque, al ejecutarse primero, las otras invocaciones se benefician de tener datos potencialmente almacenados (ya sea en la máquina local o en cualquier proxy transparente entre usted y el destino) y será más rápido. Además, bajo las condiciones correctasvar response = httpClient.GetAsync("http://localhost:9000/api/values/").Result;
puede resultar en un punto muerto debido a que agota los hilos del conjunto de hilos. Nunca debe bloquear una actividad que depende del grupo de subprocesos en los subprocesos de ThreadPool, en suawait
lugar debe hacerlo para que devuelva el subproceso al grupo.Respuestas:
Vivo en los mundos F # y Web API.
Están sucediendo muchas cosas buenas con la API web, especialmente en forma de controladores de mensajes para seguridad, etc.
Sé que la mía es solo una opinión, pero solo recomendaría usarla
HttpClient
para cualquier trabajo futuro . Quizás haya alguna manera de aprovechar algunas de las otras piezas que salenSystem.Net.Http
sin usar ese ensamblaje directamente, pero no puedo imaginar cómo funcionaría eso en este momento.Hablando de comparar estos dos
Si está utilizando .NET 4.5, utilice la bondad asincrónica con HttpClient que Microsoft proporciona a los desarrolladores. HttpClient es muy simétrico para los hermanos del servidor del HTTP, estos son HttpRequest y HttpResponse.
Actualización: 5 razones para usar la nueva API HttpClient:
Referencia
C # 5.0 Joseph Albahari
(Channel9 - Video Build 2013)
Cinco grandes razones para usar la nueva API HttpClient para conectarse a servicios web
WebClient vs HttpClient vs HttpWebRequest
fuente
WebClient
parece tener métodos asincrónicos ahora.WebClient
que no está disponible en.Net Core
peroHttpClient
está.HttpClient es la más nueva de las API y tiene los beneficios de
Si está escribiendo un servicio web que está realizando llamadas REST a otros servicios web, debería utilizar un modelo de programación asíncrono para todas sus llamadas REST, de modo que no se encuentre con el hambre de subprocesos. Probablemente también desee utilizar el compilador C # más nuevo que tiene soporte asíncrono / espera.
Nota: No es más eficiente AFAIK. Probablemente tenga un rendimiento similar si crea una prueba justa.
fuente
En primer lugar, no soy una autoridad en WebClient vs. HttpClient, específicamente. En segundo lugar, según sus comentarios anteriores, parece sugerir que WebClient es SINCRONIZADO SOLAMENTE mientras que HttpClient es ambos.
Veo eso como una gran diferencia cuando se piensa en el futuro, es decir, procesos de larga ejecución, GUI receptiva, etc.
fuente
WebClient
parece tener capacidades asíncronas en las últimas versiones de .NET. Me gustaría saber por qué parece estar superando a HttpClient en una escala tan masiva.HttpClientFactory
Es importante evaluar las diferentes formas en que puede crear un HttpClient, y parte de eso es comprender HttpClientFactory.
https://docs.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.fuente
Tengo un punto de referencia entre HttpClient, WebClient, HttpWebResponse y luego llamo Rest Web Api
y resultado Llamada de referencia web Api Benchmark
--------------------- Etapa 1 ---- 10 Solicitud
{00: 00: 17.2232544} ====> HttpClinet
{00: 00: 04.3108986} ====> Solicitud web
{00: 00: 04.5436889} ====> Cliente web
--------------------- Etapa 1 ---- 10 Solicitud - Tamaño pequeño
{00: 00: 17.2232544} ====> HttpClinet
{00: 00: 04.3108986} ====> Solicitud web
{00: 00: 04.5436889} ====> Cliente web
--------------------- Etapa 3 ---- 10 Solicitud de sincronización - Tamaño pequeño
{00: 00: 15.3047502} ====> HttpClinet
{00: 00: 03.5505249} ====> Solicitud web
{00: 00: 04.0761359} ====> Cliente web
--------------------- Etapa 4 ---- Solicitud de sincronización 100 - Tamaño pequeño
{00: 03: 23.6268086} ====> HttpClinet
{00: 00: 47.1406632} ====> Solicitud web
{00: 01: 01.2319499} ====> Cliente web
--------------------- Etapa 5 ---- 10 Solicitud de sincronización - Tamaño máximo
{00: 00: 58.1804677} ====> HttpClinet
{00: 00: 58.0710444} ====> Solicitud web
{00: 00: 38.4170938} ====> Cliente web
--------------------- Etapa 6 ---- 10 Solicitud de sincronización - Tamaño máximo
{00: 01: 04.9964278} ====> HttpClinet
{00: 00: 59.1429764} ====> Solicitud web
{00: 00: 32.0584836} ====> Cliente web
_____ WebClient es más rápido ()
// ------------------------- Funciones
fuente
Quizás podrías pensar en el problema de una manera diferente.
WebClient
yHttpClient
son esencialmente implementaciones diferentes de la misma cosa. Lo que recomiendo es implementar el patrón de inyección de dependencia con un contenedor de IoC en toda la aplicación. Debe construir una interfaz de cliente con un mayor nivel de abstracción que la transferencia HTTP de bajo nivel. Puede escribir clases concretas que usen ambosWebClient
yHttpClient
, y luego usar el contenedor IoC para inyectar la implementación a través de config.Lo que esto le permitiría hacer sería cambiar
HttpClient
y cambiarWebClient
fácilmente para poder realizar pruebas objetivas en el entorno de producción.Entonces preguntas como:
En realidad, se puede responder objetivamente cambiando entre las dos implementaciones de cliente utilizando el contenedor IoC. Aquí hay una interfaz de ejemplo de la que puede depender que no incluye ningún detalle sobre
HttpClient
oWebClient
.Código completo
Implementación de HttpClient
Puede utilizar
Task.Run
paraWebClient
ejecutar de forma asincrónica en su implementación.La inyección de dependencia, cuando se hace bien, ayuda a aliviar el problema de tener que tomar decisiones de bajo nivel por adelantado. En última instancia, la única forma de saber la verdadera respuesta es intentarlo en un entorno en vivo y ver cuál funciona mejor. Es muy posible que
WebClient
funcione mejor para algunos clientes yHttpClient
que funcione mejor para otros. Por eso es importante la abstracción. Significa que el código puede intercambiarse rápidamente o cambiarse con la configuración sin cambiar el diseño fundamental de la aplicación.fuente
Opinión impopular de 2020:
Cuando se trata de aplicaciones ASP.NET , todavía prefiero
WebClient
a las siguientesHttpClient
porque:fuente