tutorial postasync getasync example ejemplo dotnet async c# .net-4.5 idisposable using dotnet-httpclient

getasync - postasync httpclient c#



¿Es necesario eliminar HttpClient y HttpClientHandler? (11)

System.Net.Http.HttpClient y System.Net.Http.HttpClientHandler en .NET Framework 4.5 implementan IDisposable (vía System.Net.Http.HttpMessageInvoker )

La documentación de la declaración de using dice:

Como regla general, cuando utiliza un objeto IDisposable, debe declararlo y crear una instancia en una instrucción using.

Esta respuesta utiliza este patrón:

var baseAddress = new Uri("http://example.com"); var cookieContainer = new CookieContainer(); using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer }) using (var client = new HttpClient(handler) { BaseAddress = baseAddress }) { var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("foo", "bar"), new KeyValuePair<string, string>("baz", "bazinga"), }); cookieContainer.Add(baseAddress, new Cookie("CookieName", "cookie_value")); var result = client.PostAsync("/test", content).Result; result.EnsureSuccessStatusCode(); }

Pero los ejemplos más visibles de Microsoft no llaman a Dispose() explícita o implícita. Por ejemplo:

En los comentarios del anuncio , alguien le preguntó al empleado de Microsoft:

Después de revisar sus muestras, vi que no realizó la acción de disponer en la instancia de HttpClient. He usado todas las instancias de HttpClient con el uso de la declaración en mi aplicación y pensé que es la forma correcta, ya que HttpClient implementa la interfaz IDisposable. ¿Estoy en el camino correcto?

Su respuesta fue:

En general, eso es correcto, aunque hay que tener cuidado al "usar" y asincronizar, ya que no se mezclan realmente en .Net 4. En .Net 4.5 puede usar "esperar" dentro de una declaración de "usar".

Por cierto, puede reutilizar el mismo HttpClient tantas veces como desee, por lo general, no las creará ni las desechará todo el tiempo.

El segundo párrafo es superfluo para esta pregunta, que no le preocupa cuántas veces puede usar una instancia de HttpClient, pero si es necesario desecharla después de que ya no la necesite.

(Actualización: de hecho, el segundo párrafo es la clave de la respuesta, como se indica a continuación en @DPeden).

Así que mis preguntas son:

  1. ¿Es necesario, dada la implementación actual (.NET Framework 4.5), para llamar a Dispose () en las instancias HttpClient y HttpClientHandler? Aclaración: por "necesario" quiero decir si hay alguna consecuencia negativa por no disponer, como la fuga de recursos o los riesgos de corrupción de datos.

  2. Si no es necesario, ¿sería una "buena práctica" de todos modos, ya que implementan IDisposable?

  3. Si es necesario (o recomendado), ¿ este código mencionado anteriormente lo implementa de manera segura (para .NET Framework 4.5)?

  4. Si estas clases no requieren llamar a Dispose (), ¿por qué se implementaron como IDisposable?

  5. Si lo requieren, o si es una práctica recomendada, ¿son los ejemplos de Microsoft engañosos o inseguros?


No es necesario llamar a Dispose porque HttpClient hereda la clase HttpMessageInvoker y la clase HttpMessageInvoker implementa la Interfaz IDispos y HttpClientHandler hereda la clase HttpMessageHandler y la interfaz HttpMessageHandler implementan la interfaz IDisposal


Creo que uno debería usar el patrón de singleton para evitar tener que crear instancias del HttpClient y cerrarlo todo el tiempo. Si está utilizando .Net 4.0, puede usar un código de muestra como se muestra a continuación. para obtener más información sobre el patrón de singleton verifique here

class HttpClientSingletonWrapper : HttpClient { private static readonly Lazy<HttpClientSingletonWrapper> Lazy= new Lazy<HttpClientSingletonWrapper>(()=>new HttpClientSingletonWrapper()); public static HttpClientSingletonWrapper Instance {get { return Lazy.Value; }} private HttpClientSingletonWrapper() { } }

Usa el código de la siguiente manera.

var client = HttpClientSingletonWrapper.Instance;


Dispose () llama al código siguiente, que cierra las conexiones abiertas por la instancia de HttpClient. El código fue creado por descompilación con dotPeek.

HttpClientHandler.cs - Eliminar

ServicePointManager.CloseConnectionGroups(this.connectionGroupName);

Si no llama a disposición, ServicePointManager.MaxServicePointIdleTime, que se ejecuta por un temporizador, cerrará las conexiones http. El valor predeterminado es de 100 segundos.

ServicePointManager.cs

internal static readonly TimerThread.Callback s_IdleServicePointTimeoutDelegate = new TimerThread.Callback(ServicePointManager.IdleServicePointTimeoutCallback); private static volatile TimerThread.Queue s_ServicePointIdlingQueue = TimerThread.GetOrCreateQueue(100000); private static void IdleServicePointTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context) { ServicePoint servicePoint = (ServicePoint) context; if (Logging.On) Logging.PrintInfo(Logging.Web, SR.GetString("net_log_closed_idle", (object) "ServicePoint", (object) servicePoint.GetHashCode())); lock (ServicePointManager.s_ServicePointTable) ServicePointManager.s_ServicePointTable.Remove((object) servicePoint.LookupString); servicePoint.ReleaseAllConnectionGroups(); }

Si no ha establecido el tiempo de inactividad en infinito, entonces parece seguro que no debe disponer de llamadas y dejar que el temporizador de conexión inactiva se active y cierre las conexiones para usted, aunque sería mejor para usted disponer de una declaración de uso si sabe que ha terminado con una instancia de HttpClient y libere los recursos más rápido.


El consenso general es que no es necesario (no debería) deshacerse de HttpClient.

Muchas personas que están íntimamente involucradas en la forma en que funciona han declarado esto.

Consulte la publicación del blog de Darrel Miller y una publicación de SO relacionada: Resultados de rastreo de HttpClient en la pérdida de memoria para referencia.

También le sugiero encarecidamente que lea el capítulo HttpClient de Designing Evolvable Web APIs con ASP.NET para conocer el contexto de lo que sucede debajo del capó, en particular la sección "Ciclo de vida" citada aquí:

Aunque HttpClient implementa indirectamente la interfaz IDisposable, el uso estándar de HttpClient es no desecharla después de cada solicitud. El objeto HttpClient está destinado a vivir mientras su aplicación necesite realizar solicitudes HTTP. La existencia de un objeto en múltiples solicitudes habilita un lugar para configurar DefaultRequestHeaders y evita que tenga que volver a especificar cosas como CredentialCache y CookieContainer en cada solicitud, como fue necesario con HttpWebRequest.

O incluso abrir DotPeek.


El uso de la inyección de dependencias en su constructor facilita la administración de la vida útil de su HttpClient , haciendo que la gestión de la vida útil quede fuera del código que lo necesita y se pueda cambiar fácilmente en una fecha posterior.

Mi preferencia actual es crear una clase de cliente http separada que herede de HttpClient una vez por dominio de punto final de destino y luego convertirla en un singleton utilizando la inyección de dependencia. public class ExampleHttpClient : HttpClient { ... }

Luego tomo una dependencia de constructor del cliente http personalizado en las clases de servicio donde necesito acceso a esa API. Esto resuelve el problema de la vida útil y tiene ventajas cuando se trata de la agrupación de conexiones.

Puede ver un ejemplo trabajado en la respuesta relacionada en https://.com/a/50238944/3140853


En el uso típico (respuestas <2GB) no es necesario disponer los mensajes HttpResponse.

Los tipos de retorno de los métodos de HttpClient se deben desechar si su Contenido de Stream no es completamente leído. De lo contrario, no hay forma de que el CLR sepa que esas transmisiones pueden cerrarse hasta que se recojan de basura.

  • Si está leyendo los datos en un byte [] (por ejemplo, GetByteArrayAsync) o en una cadena, se leen todos los datos, por lo que no es necesario eliminarlos.
  • Las otras sobrecargas por defecto leerán el Stream hasta 2GB (HttpCompletionOption es ResponseContentRead, HttpClient.MaxResponseContentBufferSize, el valor predeterminado es 2GB)

Si configura HttpCompletionOption en ResponseHeadersRead o la respuesta es más grande que 2GB, debe limpiar. Esto se puede hacer llamando a Dispose en HttpResponseMessage o llamando a Dispose / Close en la secuencia obtenida del contenido de HttpResonseMessage o leyendo el contenido completamente.

Si llama a Dispose en el HttpClient depende de si desea cancelar las solicitudes pendientes o no.


En mi caso, estaba creando un HttpClient dentro de un método que hizo la llamada de servicio. Algo como:

public void DoServiceCall() { var client = new HttpClient(); await client.PostAsync(); }

En un rol de trabajador de Azure, después de llamar repetidamente a este método (sin disponer del HttpClient), eventualmente fallaría con SocketException (intento de conexión fallido).

Hice de HttpClient una variable de instancia (eliminándola a nivel de clase) y el problema desapareció. Así que diría, sí, desechar el HttpClient, asumiendo que es seguro (no tiene llamadas asíncronas pendientes) para hacerlo.


En mi entendimiento, llamar a Dispose() es necesario solo cuando está bloqueando los recursos que necesita más tarde (como una conexión en particular). Siempre se recomienda liberar los recursos que ya no está utilizando, incluso si no los necesita de nuevo, simplemente porque generalmente no debería conservar los recursos que no está usando (destinado a un juego de palabras).

El ejemplo de Microsoft no es incorrecto, necesariamente. Todos los recursos utilizados se liberarán cuando la aplicación salga. Y en el caso de ese ejemplo, eso sucede casi inmediatamente después de que se utiliza el HttpClient . En casos similares, llamar explícitamente a Dispose() es algo superfluo.

Pero, en general, cuando una clase implementa IDisposable , el entendimiento es que debe Dispose() sus instancias tan pronto como esté listo y capaz. Yo diría que esto es particularmente cierto en casos como HttpClient donde no se documenta explícitamente si los recursos o las conexiones se mantienen o se abren. En el caso en el que la conexión se volverá a utilizar [pronto], querrá renunciar a Dipose() , no está "completamente preparado" en ese caso.

Consulte también: Método IDisposable.Disponer y cuándo llamar a Dispose


Las respuestas actuales son un poco confusas y engañosas, y faltan algunas implicaciones de DNS importantes. Intentaré resumir dónde están las cosas claramente.

  1. En términos generales, la mayoría de los objetos IDisposable deberían eliminarse cuando haya terminado con ellos , especialmente aquellos que poseen recursos de SO con nombre / compartidos . HttpClient no es una excepción, ya que, como señala Darrel Miller , asigna tokens de cancelación, y los cuerpos de solicitud / respuesta pueden ser flujos no administrados.
  2. Sin embargo, la mejor práctica para HttpClient dice que debe crear una instancia y reutilizarla tanto como sea posible (utilizando sus miembros seguros para subprocesos en escenarios de subprocesos múltiples). Por lo tanto, en la mayoría de los escenarios nunca lo desechará simplemente porque lo necesitará todo el tiempo .
  3. El problema con la reutilización del mismo HttpClient "para siempre" es que la conexión HTTP subyacente puede permanecer abierta contra la IP originalmente resuelta por el DNS, independientemente de los cambios del DNS . Esto puede ser un problema en escenarios como la implementación azul / verde y la conmutación por error basada en DNS . Existen varios enfoques para tratar este problema, el más confiable es el que involucra al servidor que envía una Connection:close encabezado después de que se realicen los cambios de DNS. Otra posibilidad consiste en reciclar el HttpClient en el lado del cliente, ya sea periódicamente o a través de algún mecanismo que aprende sobre el cambio de DNS. Consulte https://github.com/dotnet/corefx/issues/11224 para obtener más información (sugiero que lo lea atentamente antes de usar ciegamente el código sugerido en la publicación del blog vinculado).

Si desea deshacerse de HttpClient, puede hacerlo si lo configura como un grupo de recursos. Y al final de su aplicación, dispone su grupo de recursos.

Código:

// Notice that IDisposable is not implemented here! public interface HttpClientHandle { HttpRequestHeaders DefaultRequestHeaders { get; } Uri BaseAddress { get; set; } // ... // All the other methods from peeking at HttpClient } public class HttpClientHander : HttpClient, HttpClientHandle, IDisposable { public static ConditionalWeakTable<Uri, HttpClientHander> _httpClientsPool; public static HashSet<Uri> _uris; static HttpClientHander() { _httpClientsPool = new ConditionalWeakTable<Uri, HttpClientHander>(); _uris = new HashSet<Uri>(); SetupGlobalPoolFinalizer(); } private DateTime _delayFinalization = DateTime.MinValue; private bool _isDisposed = false; public static HttpClientHandle GetHttpClientHandle(Uri baseUrl) { HttpClientHander httpClient = _httpClientsPool.GetOrCreateValue(baseUrl); _uris.Add(baseUrl); httpClient._delayFinalization = DateTime.MinValue; httpClient.BaseAddress = baseUrl; return httpClient; } void IDisposable.Dispose() { _isDisposed = true; GC.SuppressFinalize(this); base.Dispose(); } ~HttpClientHander() { if (_delayFinalization == DateTime.MinValue) _delayFinalization = DateTime.UtcNow; if (DateTime.UtcNow.Subtract(_delayFinalization) < base.Timeout) GC.ReRegisterForFinalize(this); } private static void SetupGlobalPoolFinalizer() { AppDomain.CurrentDomain.ProcessExit += (sender, eventArgs) => { FinalizeGlobalPool(); }; } private static void FinalizeGlobalPool() { foreach (var key in _uris) { HttpClientHander value = null; if (_httpClientsPool.TryGetValue(key, out value)) try { value.Dispose(); } catch { } } _uris.Clear(); _httpClientsPool = null; } }

controlador de var = HttpClientHander.GetHttpClientHandle (nuevo Uri ("url base")).

  • HttpClient, como interfaz, no puede llamar a Dispose ().
  • El recolector de basura llamará a Dispose () con retraso. O cuando el programa limpia el objeto a través de su destructor.
  • Usa referencias débiles + demora en la lógica de limpieza, por lo que permanece en uso mientras se reutiliza con frecuencia.
  • Solo asigna un nuevo HttpClient para cada URL base que se le pasa. Las razones explicadas por Ohad Schneider responden a continuación. Mal comportamiento al cambiar la url base.
  • HttpClientHandle permite simulación en pruebas

Respuesta corta: No, la declaración en la respuesta actualmente aceptada NO es precisa : "El consenso general es que usted no (no debería) necesita deshacerse de HttpClient".

Respuesta larga : AMBAS de las siguientes afirmaciones son verdaderas y alcanzables al mismo tiempo:

  1. "HttpClient está destinado a ser instanciado una vez y reutilizado a lo largo de la vida de una aplicación", citado en la documentación oficial .
  2. Se supone / recomienda que un objeto IDisposable se IDisposable .

Y NO SON CONFLICTOS NECESARIOS entre sí. Es solo una cuestión de cómo organizar su código para reutilizar un HttpClient Y aún así disponerlo adecuadamente.

Una respuesta aún más larga citada de mi otra respuesta :

No es una coincidencia ver a la gente en algunas publicaciones del blog culpando a cómo la interfaz IDisposable HttpClient hace que utilicen el patrón de using (var client = new HttpClient()) {...} y luego conducir al problema del controlador de socket agotado.

Creo que se trata de una concepción tácita (¿errónea?): "Se espera que un objeto IDisponible sea de corta duración" .

SIN EMBARGO, aunque ciertamente parece algo de corta duración cuando escribimos código en este estilo:

using (var foo = new SomeDisposableObject()) { ... }

La documentación oficial sobre IDisposable nunca menciona los objetos IDisposable tienen que ser de corta duración. Por definición, IDisposable es simplemente un mecanismo que le permite liberar recursos no administrados. Nada mas. En ese sentido, se ESPERA que eventualmente se desencadene la eliminación, pero no es necesario que lo haga de manera breve.

Por lo tanto, es su trabajo elegir correctamente cuándo desencadenar la eliminación, basándose en el requisito del ciclo de vida de su objeto real. No hay nada que le impida utilizar un IDisposable de una manera duradera:

using System; namespace HelloWorld { class Hello { static void Main() { Console.WriteLine("Hello World!"); using (var client = new HttpClient()) { for (...) { ... } // A really long loop // Or you may even somehow start a daemon here } // Keep the console window open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } } }

Con esta nueva comprensión, ahora volvemos a visitar la publicación del blog , podemos notar claramente que el "arreglo" inicializa HttpClient una vez, pero nunca lo desechamos, es por eso que podemos ver en su salida netstat que, la conexión permanece en el estado ESTABLECIDO, lo que significa que NO se ha cerrado correctamente. Si estuviera cerrado, su estado estaría en TIME_WAIT en su lugar. En la práctica, no es un gran problema filtrar solo una conexión después de que finalice todo el programa, y ​​el póster del blog sigue viendo un aumento de rendimiento después de la corrección; pero aún así, es conceptualmente incorrecto culpar a IDisposable y elegir NO desecharlo.