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.