Llego tarde a la fiesta, pero aquí está mi viaje de aprendizaje sobre este tema complicado.
1. ¿Dónde podemos encontrar al defensor oficial sobre la reutilización de HttpClient?
Quiero decir, si se pretende reutilizar HttpClient
y hacerlo es importante , dicho defensor está mejor documentado en su propia documentación API, en lugar de estar oculto en muchos "Temas avanzados", "Patrón de rendimiento (anti)" u otras publicaciones de blog. . De lo contrario, ¿cómo se supone que un nuevo alumno debe saberlo antes de que sea demasiado tarde?
A partir de ahora (mayo de 2018), el primer resultado de búsqueda al buscar en Google "c # httpclient" apunta a esta página de referencia de API en MSDN , que no menciona esa intención en absoluto. Bueno, la lección 1 aquí para los novatos es, siempre haga clic en el enlace "Otras versiones" justo después del título de la página de ayuda de MSDN, probablemente encontrará enlaces a la "versión actual" allí. En este caso de HttpClient, lo llevará al último documento
que contiene esa descripción de intención .
Sospecho que muchos desarrolladores que eran nuevos en este tema tampoco encontraron la página de documentación correcta, es por eso que este conocimiento no está muy extendido, y la gente se sorprendió cuando lo descubrieron más
tarde , posiblemente de una manera difícil .
2. La concepción (¿equivocada?) De using
IDisposable
Este es un poco fuera de tema, pero aún vale la pena señalar que, no es una coincidencia ver a las personas en esas publicaciones de blog mencionadas culpando cómo HttpClient
la IDisposable
interfaz hace que tienden a usar el using (var client = new HttpClient()) {...}
patrón y luego conducen al problema.
Creo que eso se reduce a una concepción tácita (¿equivocada?):
"Se espera que un objeto IDisposable 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 que los IDisposable
objetos tienen que ser de corta duración. Por definición, IDisposable es simplemente un mecanismo para permitirle liberar recursos no administrados. Nada mas. En ese sentido, se ESPERA que eventualmente active la eliminación, pero no requiere que lo haga de manera efímera.
Por lo tanto, es su trabajo elegir adecuadamente cuándo activar la eliminación, basándose en el requisito del ciclo de vida de su objeto real. No hay nada que le impida usar un ID desechable 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 que volvemos a visitar esa publicación de blog , podemos notar claramente que la "corrección" se inicializa HttpClient
una vez, pero nunca la elimina, por eso podemos ver en su salida de netstat que, la conexión permanece en estado ESTABLECIDO, lo que significa que tiene 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 abierta después de que termine todo el programa, y el póster del blog aún ve una ganancia de rendimiento después de la solución; pero aún así, es conceptualmente incorrecto culpar a IDisposable y elegir NO desecharlo.
3. ¿Tenemos que poner HttpClient en una propiedad estática, o incluso ponerlo como un singleton?
Basado en la comprensión de la sección anterior, creo que la respuesta aquí se vuelve clara: "no necesariamente". Realmente depende de cómo organice su código, siempre que reutilice un HttpClient Y (idealmente) lo elimine eventualmente.
Hilarantemente, ni siquiera el ejemplo en la
sección de Comentarios del documento oficial actual lo
hace estrictamente correcto. Define una clase "GoodController", que contiene una propiedad HttpClient estática que no se eliminará; que desobedece lo que otro ejemplo en la sección de Ejemplos
enfatiza: "necesita llamar a disponer ... para que la aplicación no pierda recursos".
Y, por último, el singleton no está exento de desafíos propios.
"¿Cuántas personas piensan que la variable global es una buena idea? Nadie.
¿Cuántas personas piensan que Singleton es una buena idea? Unos pocos.
¿Lo que da? Los singletons son solo un montón de variables globales ".
- Citado de esta charla inspiradora, "Global State and Singletons"
PS: SqlConnection
Este es irrelevante para las preguntas y respuestas actuales, pero probablemente sea bueno saberlo. El patrón de uso de SqlConnection es diferente. Usted NO tiene que volver a utilizar SqlConnection , ya que se encargará de su grupo de conexión mejor de esa manera.
La diferencia es causada por su enfoque de implementación. Cada instancia de HttpClient usa su propio grupo de conexiones (citado desde
aquí ); pero SqlConnection en sí es administrado por un grupo de conexiones central, de acuerdo con esto .
Y aún debe deshacerse de SqlConnection, igual que se supone que debe hacer para HttpClient.