¿Cuál debería ser la HttpClient
vida útil de un cliente WebAPI?
¿Es mejor tener una instancia de HttpClient
para múltiples llamadas?
¿Cuál es la sobrecarga de crear y eliminar una HttpClient
solicitud por solicitud, como en el ejemplo a continuación (tomado de http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from- a-net-client ):
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// New code:
HttpResponseMessage response = await client.GetAsync("api/products/1");
if (response.IsSuccessStatusCode)
{
Product product = await response.Content.ReadAsAsync<Product>();
Console.WriteLine("{0}\t${1}\t{2}", product.Name, product.Price, product.Category);
}
}
c#
asp.net
web-services
asp.net-web-api
dotnet-httpclient
Bruno Pessanha
fuente
fuente
Stopwatch
Sin embargo, no estoy seguro, podría usar la clase para compararla. Mi estimación sería que tiene más sentido tener una solaHttpClient
, suponiendo que todas esas instancias se usen en el mismo contexto.Respuestas:
HttpClient
ha sido diseñado para ser reutilizado para múltiples llamadas . Incluso a través de múltiples hilos. ElHttpClientHandler
tiene credenciales y las galletas que están destinados a ser reutilizada a través de las llamadas. Tener una nuevaHttpClient
instancia requiere volver a configurar todo eso. Además, laDefaultRequestHeaders
propiedad contiene propiedades destinadas a múltiples llamadas. Tener que restablecer esos valores en cada solicitud derrota el punto.Otro beneficio importante de
HttpClient
es la capacidad de agregarHttpMessageHandlers
a la tubería de solicitud / respuesta para aplicar preocupaciones transversales. Estos podrían ser para el registro, la auditoría, la limitación, el manejo de redireccionamiento, el manejo fuera de línea, la captura de métricas. Todo tipo de cosas diferentes. Si se crea un nuevo HttpClient en cada solicitud, entonces todos estos controladores de mensajes deben configurarse en cada solicitud y de alguna manera también se debe proporcionar cualquier estado de nivel de aplicación que se comparta entre las solicitudes de estos controladores.Cuanto más utilice las funciones
HttpClient
, más verá que reutilizar una instancia existente tiene sentido.Sin embargo, el mayor problema, en mi opinión, es que cuando
HttpClient
se elimina una clase, se eliminaHttpClientHandler
, lo que luego cierra por la fuerza laTCP/IP
conexión en el conjunto de conexiones que administraServicePointManager
. Esto significa que cada solicitud con una nuevaHttpClient
requiere restablecer una nuevaTCP/IP
conexión.Según mis pruebas, usando HTTP simple en una LAN, el impacto en el rendimiento es bastante insignificante. Sospecho que esto se debe a que hay un keepalive TCP subyacente que mantiene la conexión abierta incluso cuando
HttpClientHandler
intenta cerrarla.En las solicitudes que pasan por Internet, he visto una historia diferente. He visto un impacto de rendimiento del 40% debido a que tengo que volver a abrir la solicitud cada vez.
Sospecho que el golpe en una
HTTPS
conexión sería aún peor.Mi consejo es mantener una instancia de HttpClient durante toda la vida útil de su aplicación para cada API distinta a la que se conecte.
fuente
which then forcibly closes the TCP/IP connection in the pool of connections that is managed by ServicePointManager
¿Qué tan seguro está sobre esta declaración? Eso es difícil de creer.HttpClient
me parece una unidad de trabajo que se supone que se instancia a menudo.Si desea que su aplicación se amplíe, ¡la diferencia es ENORME! Dependiendo de la carga, verá números de rendimiento muy diferentes. Como menciona Darrel Miller, el HttpClient fue diseñado para ser reutilizado en todas las solicitudes. Esto fue confirmado por los chicos del equipo de BCL que lo escribieron.
Un proyecto reciente que tuve fue ayudar a un minorista informático en línea muy grande y conocido a escalar el tráfico del Viernes Negro / vacaciones para algunos sistemas nuevos. Nos encontramos con algunos problemas de rendimiento relacionados con el uso de HttpClient. Como se implementa
IDisposable
, los desarrolladores hicieron lo que normalmente harían al crear una instancia y colocarla dentro de unausing()
declaración. Una vez que comenzamos las pruebas de carga, la aplicación puso de rodillas al servidor; sí, el servidor no solo la aplicación. La razón es que cada instancia de HttpClient abre un puerto en el servidor. Debido a la finalización no determinista de GC y al hecho de que está trabajando con recursos informáticos que abarcan múltiples capas OSI , el cierre de puertos de red puede llevar un tiempo. De hecho, el propio sistema operativo WindowsPuede tomar hasta 20 segundos para cerrar un puerto (por Microsoft). Estábamos abriendo puertos más rápido de lo que podrían cerrarse: agotamiento del puerto del servidor que perjudicó la CPU al 100%. Mi solución fue cambiar el HttpClient a una instancia estática que resolvió el problema. Sí, es un recurso desechable, pero cualquier sobrecarga se ve ampliamente compensada por la diferencia en el rendimiento. Te animo a que hagas algunas pruebas de carga para ver cómo se comporta tu aplicación.También puede consultar la página de guía de WebAPI para obtener documentación y ejemplos en https://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
Presta especial atención a esta llamada:
Si encuentra que necesita usar una estática
HttpClient
con diferentes encabezados, direcciones base, etc., lo que tendrá que hacer es crear elHttpRequestMessage
manual y establecer esos valores en elHttpRequestMessage
. Luego, use elHttpClient:SendAsync(HttpRequestMessage requestMessage, ...)
ACTUALIZACIÓN para .NET Core : debe usar la
IHttpClientFactory
inyección de dependencia vía para crearHttpClient
instancias. Administrará la vida útil por usted y no necesita deshacerse de él explícitamente. Consulte Realizar solicitudes HTTP utilizando IHttpClientFactory en ASP.NET Corefuente
Como dicen las otras respuestas,
HttpClient
está destinado a la reutilización. Sin embargo, la reutilización de una solaHttpClient
instancia en una aplicación multiproceso significa que no puede cambiar los valores de sus propiedades con estado, comoBaseAddress
yDefaultRequestHeaders
(por lo que solo puede usarlas si son constantes en su aplicación).Un enfoque para sortear esta limitación es envolver
HttpClient
con una clase que duplica todos losHttpClient
métodos que necesita (GetAsync
,PostAsync
etc.) y los delega en un singletonHttpClient
. Sin embargo, eso es bastante tedioso (también necesitará ajustar los métodos de extensión ), y afortunadamente hay otra forma : seguir creando nuevasHttpClient
instancias, pero reutilizando el subyacenteHttpClientHandler
. Solo asegúrese de no desechar el controlador:fuente
SendAsync
es mucho menos conveniente que los métodos dedicados comoPutAsync
,PostAsJsonAsync
etc.Relacionado con sitios web de gran volumen pero no directamente con HttpClient. Tenemos el fragmento de código a continuación en todos nuestros servicios.
Desde https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Net.ServicePoint.ConnectionLeaseTimeout);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2); k (DevLang-csharp) & rd = verdadero
"Puede usar esta propiedad para asegurarse de que las conexiones activas de un objeto de ServicePoint no permanezcan abiertas indefinidamente. Esta propiedad está destinada a escenarios en los que las conexiones deben interrumpirse y restablecerse periódicamente, como los escenarios de equilibrio de carga.
De manera predeterminada, cuando KeepAlive es verdadero para una solicitud, la propiedad MaxIdleTime establece el tiempo de espera para cerrar las conexiones de ServicePoint debido a la inactividad. Si ServicePoint tiene conexiones activas, MaxIdleTime no tiene efecto y las conexiones permanecen abiertas indefinidamente.
Cuando la propiedad ConnectionLeaseTimeout se establece en un valor distinto de -1, y después de que transcurre el tiempo especificado, se cierra una conexión de ServicePoint activa después de atender una solicitud estableciendo KeepAlive en falso en esa solicitud. Establecer este valor afecta a todas las conexiones administradas por el objeto ServicePoint ".
Cuando tiene servicios detrás de un CDN u otro punto final que desea conmutar por error, esta configuración ayuda a las personas que llaman a seguirlo a su nuevo destino. En este ejemplo, 60 segundos después de una conmutación por error, todas las personas que llaman deben volver a conectarse al nuevo punto final. Requiere que conozca sus servicios dependientes (aquellos servicios que USTED llama) y sus puntos finales.
fuente
También puede consultar esta publicación de blog de Simon Timms: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
fuente
Una cosa para señalar, que ninguno de los blogs de "no usar usando" nota es que no es solo la BaseAddress y DefaultHeader lo que debe tener en cuenta. Una vez que hace que HttpClient sea estático, hay estados internos que se llevarán a través de las solicitudes. Un ejemplo: se está autenticando a un tercero con HttpClient para obtener un token FedAuth (ignore por qué no usa OAuth / OWIN / etc.), ese mensaje de Respuesta tiene un encabezado Set-Cookie para FedAuth, esto se agrega a su estado HttpClient. El siguiente usuario que inicie sesión en su API enviará la cookie FedAuth de la última persona a menos que esté administrando estas cookies en cada solicitud.
fuente
Como primer problema, aunque esta clase es desechable, usarla con la
using
instrucción no es la mejor opción porque incluso cuando desecha elHttpClient
objeto, el socket subyacente no se libera de inmediato y puede causar un problema grave llamado 'agotamiento de sockets'.Pero hay un segundo problema
HttpClient
que puede tener cuando lo usa como objeto único o estático. En este caso, un singleton o staticHttpClient
no respeta losDNS
cambios.en .net core puedes hacer lo mismo con HttpClientFactory algo como esto:
Configurar servicios
documentación y ejemplo aquí
fuente