HttpClient et HttpClientHandler doivent-ils être supprimés entre les demandes?

334

System.Net.Http.HttpClient et System.Net.Http.HttpClientHandler dans .NET Framework 4.5 implémentent IDisposable (via System.Net.Http.HttpMessageInvoker ).

La usingdocumentation de la déclaration indique:

En règle générale, lorsque vous utilisez un objet IDisposable, vous devez le déclarer et l'instancier dans une instruction using.

Cette réponse utilise ce modèle:

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();
}

Mais les exemples les plus visibles de Microsoft n'appellent pas Dispose()explicitement ou implicitement. Par exemple:

Dans les commentaires de l' annonce , quelqu'un a demandé à l'employé de Microsoft:

Après avoir vérifié vos échantillons, j'ai vu que vous n'avez pas effectué l'action d'élimination sur l'instance HttpClient. J'ai utilisé toutes les instances de HttpClient avec l'instruction using sur mon application et j'ai pensé que c'était la bonne façon puisque HttpClient implémente l'interface IDisposable. Suis-je sur la bonne voie?

Sa réponse a été:

En général, c'est correct, bien que vous deviez faire attention à «utiliser» et asynchroniser car ils ne se mélangent pas vraiment dans .Net 4, dans .Net 4.5, vous pouvez utiliser «attendre» dans une instruction «using».

Btw, vous pouvez réutiliser le même HttpClient autant de fois que vous le souhaitez, donc généralement vous ne les créerez / supprimerez pas tout le temps.

Le deuxième paragraphe est superflu pour cette question, qui ne concerne pas le nombre de fois où vous pouvez utiliser une instance HttpClient, mais s'il est nécessaire de la supprimer une fois que vous n'en avez plus besoin.

(Mise à jour: en fait, le deuxième paragraphe est la clé de la réponse, comme indiqué ci-dessous par @DPeden.)

Mes questions sont donc:

  1. Est-il nécessaire, compte tenu de l'implémentation actuelle (.NET Framework 4.5), d'appeler Dispose () sur les instances HttpClient et HttpClientHandler? Clarification: par "nécessaire", je veux dire s'il y a des conséquences négatives pour ne pas disposer, comme des risques de fuite de ressources ou de corruption de données.

  2. Si ce n'est pas nécessaire, serait-ce de toute façon une "bonne pratique", puisqu'ils implémentent IDisposable?

  3. S'il est nécessaire (ou recommandé), ce code mentionné ci-dessus le met-il en œuvre en toute sécurité (pour .NET Framework 4.5)?

  4. Si ces classes ne nécessitent pas d'appeler Dispose (), pourquoi ont-elles été implémentées comme IDisposable?

  5. S'ils l'exigent ou s'il s'agit d'une pratique recommandée, les exemples Microsoft sont-ils trompeurs ou dangereux?

Fernando Correia
la source
2
@Damien_The_Unbeliever, merci pour vos commentaires. Avez-vous des suggestions sur la façon dont je pourrais clarifier la question? Je veux savoir si cela peut entraîner des problèmes généralement associés à la non-élimination des ressources, tels que la fuite de ressources et la corruption de données.
Fernando Correia
9
@Damien_The_Unbeliever: Pas vrai. En particulier, les rédacteurs de flux doivent être disposés à avoir un comportement correct.
Stephen Cleary
1
@StephenCleary - à quels aspects pensez-vous? Certes, vous pouvez en appeler Flushun après chaque écriture, et à part l'inconvénient de continuer à conserver les ressources sous-jacentes plus longtemps que nécessaire, que ne se produira-t-il pas qui est requis pour un "comportement correct"?
Damien_The_Unbeliever
1
C'est tout à fait faux: "En règle générale, lorsque vous utilisez un objet IDisposable, vous devez le déclarer et l'instancier dans une instruction using". Je lirais toujours la documentation sur la classe implémentant IDisposable avant de décider si je devrais utiliser une utilisation pour cela. En tant qu'auteur de bibliothèques où j'implémente IDisposable, car il faut libérer des ressources non mélangées, je serais horrifié si les consommateurs créaient à chaque fois une instance supprimée au lieu de réutiliser une instance existante. Cela ne veut pas dire de ne pas disposer de l'instance éventuellement ..
markmnl
1
J'ai soumis un PR à Microsoft pour mettre à jour ses documents: github.com/dotnet/docs/pull/2470
markmnl

Réponses:

259

Le consensus général est que vous n'avez pas (ne devez pas) disposer de HttpClient.

Beaucoup de gens qui sont intimement impliqués dans son fonctionnement l'ont déclaré.

Voir l'article de blog de Darrel Miller et un article SO connexe: l' exploration HttpClient entraîne une fuite de mémoire pour référence.

Je vous suggère également fortement de lire le chapitre HttpClient de Designing Evolvable Web APIs avec ASP.NET pour le contexte sur ce qui se passe sous le capot, en particulier la section "Lifecycle" citée ici:

Bien que HttpClient implémente indirectement l'interface IDisposable, l'utilisation standard de HttpClient n'est pas de s'en débarrasser après chaque demande. L'objet HttpClient est destiné à vivre aussi longtemps que votre application doit effectuer des requêtes HTTP. La présence d'un objet sur plusieurs demandes permet de définir DefaultRequestHeaders et vous évite d'avoir à spécifier de nouveau des éléments tels que CredentialCache et CookieContainer sur chaque demande, comme cela était nécessaire avec HttpWebRequest.

Ou même ouvrez DotPeek.

David Peden
la source
64
Pour clarifier votre réponse, serait-il exact de dire que "vous n'avez pas besoin de vous débarrasser de HttpClient SI VOUS TENEZ À L'INSTANCE POUR LE RÉUTILISER PLUS TARD"? Par exemple, si une méthode est appelée à plusieurs reprises et crée une nouvelle instance HttpClient (même si ce n'est pas le modèle recommandé dans la plupart des cas), serait-il toujours correct de dire que cette méthode ne doit pas supprimer l'instance (qui ne sera pas réutilisée)? Cela pourrait conduire à des milliers d'instances non éliminées. En d'autres termes, que vous devriez essayer de réutiliser les instances, mais si vous ne réutilisez pas, vous feriez mieux de les éliminer (pour libérer les connexions)?
Fernando Correia
8
Je pense que la réponse frustrante mais correcte est compréhensible, cela dépend. Si je devais être épinglé pour donner des conseils généraux qui fonctionnaient dans la plupart des cas (je ne dis jamais tous), je vous suggérerais d'utiliser un conteneur IoC et d'enregistrer une instance de HttpClient en tant que singleton. La durée de vie de l'instance serait alors limitée à celle de la durée de vie du conteneur. Cela peut être défini au niveau de l'application ou peut-être par demande dans une application Web.
David Peden
25
@FernandoCorreia Oui. Si, pour une raison quelconque, vous créez et détruisez à plusieurs reprises des instances HttpClient, alors oui, vous devez les supprimer. Je ne suggère pas d'ignorer l'interface IDisposable, j'essaie simplement d'encourager les gens à réutiliser les instances.
Darrel Miller
20
Juste pour ajouter plus de crédibilité à cette réponse, j'ai parlé avec l'équipe HttpClient aujourd'hui et ils ont confirmé que HttpClient n'était pas conçu pour être utilisé à la demande. Une instance de HttpClient doit être maintenue en vie pendant qu'une application cliente continue d'interagir avec un hôte particulier.
Darrel Miller
19
@DavidPeden L'enregistrement de HttpClient en tant que singleton me semble dangereux car il est modifiable. Par exemple, tout le monde assignant à la Timeoutpropriété ne se piétinerait-il pas?
Jon-Eric
47

Les réponses actuelles sont un peu confuses et trompeuses, et il leur manque certaines implications DNS importantes. Je vais essayer de résumer clairement où en sont les choses.

  1. De manière générale, la plupart des IDisposableobjets devraient idéalement être éliminés lorsque vous en avez terminé , en particulier ceux qui possèdent des ressources de système d'exploitation nommées / partagées . HttpClientne fait pas exception, car comme le souligne Darrel Miller , il alloue des jetons d'annulation, et les corps de demande / réponse peuvent être des flux non gérés.
  2. Cependant, la meilleure pratique pour HttpClient indique que vous devez créer une instance et la réutiliser autant que possible (en utilisant ses membres thread-safe dans les scénarios multi-thread). Par conséquent, dans la plupart des scénarios, vous n'en disposerez jamais simplement parce que vous en aurez besoin tout le temps .
  3. Le problème avec la réutilisation du même HttpClient "pour toujours" est que la connexion HTTP sous-jacente peut rester ouverte par rapport à l'adresse IP initialement résolue par DNS, indépendamment des modifications DNS . Cela peut être un problème dans des scénarios comme le déploiement bleu / vert et le basculement basé sur DNS . Il existe différentes approches pour résoudre ce problème, la plus fiable impliquant l'envoi par le serveur d'un en- Connection:closetête après les modifications DNS. Une autre possibilité consiste à recycler le HttpClientcôté client, soit périodiquement, soit via un mécanisme qui apprend le changement DNS. Voir https://github.com/dotnet/corefx/issues/11224 pour plus d'informations (je suggère de le lire attentivement avant d'utiliser aveuglément le code suggéré dans le billet de blog lié).
Ohad Schneider
la source
Je dispose tout le temps car je ne peux pas changer de proxy sur une instance;)
ed22
Si vous avez besoin de supprimer le HttpClient pour une raison quelconque, vous devez conserver une instance statique de HttpMessageHandler, car la suppression de celui-ci est en fait la cause des problèmes attribués à la suppression de HttpClient. HttpClient a une surcharge de constructeur qui vous permet de spécifier que le gestionnaire fourni ne doit pas être supprimé, auquel cas vous pouvez réutiliser HttpMessageHandler avec d'autres instances HttpClient.
Tom Lint
Vous devez conserver votre HttpClient, mais vous pouvez utiliser quelque chose comme System.Net.ServicePointManager.DnsRefreshTimeout = 3000; Cela est utile, par exemple, si vous êtes sur un appareil mobile qui peut à tout moment basculer entre le wifi et la 4G.
Johan Franzén
18

À ma connaissance, l'appel Dispose()n'est nécessaire que lorsqu'il bloque les ressources dont vous avez besoin plus tard (comme une connexion particulière). Il est toujours recommandé de libérer les ressources que vous n'utilisez plus, même si vous n'en avez plus besoin, simplement parce que vous ne devriez généralement pas conserver les ressources que vous n'utilisez pas (jeu de mots voulu).

L'exemple Microsoft n'est pas incorrect, nécessairement. Toutes les ressources utilisées seront libérées à la fermeture de l'application. Et dans le cas de cet exemple, cela se produit presque immédiatement après l' HttpClientutilisation du. Dans des cas similaires, appeler explicitement Dispose()est quelque peu superflu.

Mais, en général, lorsqu'une classe est implémentée IDisposable, il est entendu que vous devez vérifier Dispose()ses instances dès que vous êtes complètement prêt et capable. Je suppose que cela est particulièrement vrai dans des cas comme ceux HttpClientoù il n'est pas explicitement documenté si les ressources ou les connexions sont conservées sur / ouvertes. Dans le cas où la connexion sera réutilisée à nouveau [bientôt], vous voudrez y renoncer Dipose()- vous n'êtes pas "entièrement prêt" dans ce cas.

Voir aussi: Méthode IDisposable.Dispose et quand appeler Dispose

svidgen
la source
7
C'est comme si quelqu'un apportait une banane dans votre maison, la mangeait et se tenait debout avec la peau. Que devraient-ils faire avec la peau? ... S'ils sortent avec ça, laissez-les partir. S'ils restent, faites-les jeter à la poubelle pour qu'ils ne puent pas l'endroit.
svidgen
juste pour clarifier cette réponse, dites-vous, "il n'est pas nécessaire de se débarrasser si le programme va se terminer juste après l'avoir utilisé"? Et que vous devez éliminer si le programme devrait continuer pendant un certain temps à faire d'autres choses?
Fernando Correia
@FernandoCorreia Oui, sauf si j'oublie quelque chose, je pense que c'est un principe sûr. Réfléchissez-y cependant dans chaque cas. Si vous travaillez avec une connexion, par exemple, vous ne voulez pas en faire Dispose()prématurément et devez vous reconnecter quelques secondes plus tard si la connexion existante est réutilisable. De même, vous ne voulez pas inutilement Dispose()d'images ou d'autres structures que vous pourriez avoir à reconstruire en une minute ou deux.
svidgen
Je comprends. Mais dans le cas particulier de HttpClient et HttpClientHandler sur lequel porte cette question, tiennent-ils une ressource ouverte telle qu'une connexion HTTP? Si c'est ce qui se passe, je devrai peut-être repenser ma façon de les utiliser.
Fernando Correia
1
@DPeden Votre réponse n'est pas du tout en conflit avec la mienne. Notez, dis-je, que vous devez supprimer () ses instances dès que vous êtes complètement prêt et capable . Si vous prévoyez d'utiliser à nouveau l'instance, vous n'êtes pas prêt .
svidgen
9

Dispose () appelle le code ci-dessous, qui ferme les connexions ouvertes par l'instance HttpClient. Le code a été créé en décompilant avec dotPeek.

HttpClientHandler.cs - Éliminer

ServicePointManager.CloseConnectionGroups(this.connectionGroupName);

Si vous n'appelez pas dispose, ServicePointManager.MaxServicePointIdleTime, qui s'exécute par une minuterie, fermera les connexions http. La valeur par défaut est 100 secondes.

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 vous n'avez pas défini le temps d'inactivité sur infini, il semble prudent de ne pas appeler dispose et de laisser le temporisateur de connexion inactive démarrer et fermer les connexions pour vous, bien qu'il serait préférable pour vous d'appeler dispose dans une instruction using si vous savez que vous avez terminé avec une instance HttpClient et libérez les ressources plus rapidement.

Timothy Gonzalez
la source
1
Vous pouvez également voir le code sur github. github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/…
TamusJRoyce
8

Réponse courte: Non, l'énoncé de la réponse actuellement acceptée n'est PAS exact : "Le consensus général est que vous n'avez pas (ne devez pas) disposer de HttpClient".

Réponse longue : LES DEUX déclarations suivantes sont vraies et réalisables en même temps:

  1. "HttpClient est destiné à être instancié une fois et réutilisé tout au long de la vie d'une application", cité dans la documentation officielle .
  2. Un IDisposableobjet est censé / recommandé d'être éliminé.

Et ils NE CONFLITENT PAS NÉCESSAIREMENT. Il s'agit simplement de savoir comment organiser votre code pour réutiliser un HttpClientAND et le disposer correctement.

Une réponse encore plus longue citée de ma autre réponse :

Ce n'est pas une coïncidence de voir des gens dans certains articles de blog blâmer la façon dont HttpClientl' IDisposableinterface les fait avoir tendance à utiliser le using (var client = new HttpClient()) {...}modèle et ensuite conduire à un problème de gestionnaire de socket épuisé.

Je crois que cela revient à une conception (inexacte?) Tacite: "un objet IDisposable devrait être de courte durée" .

CEPENDANT, alors que cela ressemble certainement à une chose de courte durée lorsque nous écrivons du code dans ce style:

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

la documentation officielle sur IDisposable ne mentionne jamais que les IDisposableobjets doivent être de courte durée. Par définition, IDisposable est simplement un mécanisme qui vous permet de libérer des ressources non gérées. Rien de plus. En ce sens, vous êtes censé déclencher éventuellement la cession, mais cela ne vous oblige pas à le faire de manière éphémère.

Il vous appartient donc de bien choisir le moment du déclenchement de la mise au rebut, en fonction des exigences du cycle de vie de votre objet réel. Rien ne vous empêche d'utiliser un IDisposable de manière durable:

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();
        }
    }
}

Avec cette nouvelle compréhension, maintenant nous revisitons ce billet de blog , nous pouvons clairement remarquer que le "correctif" s'initialise HttpClientune fois mais ne le supprime jamais, c'est pourquoi nous pouvons voir dans sa sortie netstat que, la connexion reste à l'état ESTABLISHED ce qui signifie qu'elle a PAS été correctement fermé. S'il était fermé, son état serait à la place dans TIME_WAIT. Dans la pratique, ce n'est pas grave de laisser une seule connexion ouverte après la fin de votre programme, et l'affiche du blog continue de voir un gain de performances après la correction; mais quand même, il est conceptuellement incorrect de blâmer IDisposable et de choisir de NE PAS le jeter.

RayLuo
la source
merci pour cette explication. Il fait clairement la lumière sur le consensus. D'après vous, quand pensez-vous qu'il est approprié d'appeler HttpClient.Dispose?.
Jeson Martajaya
@JesonMartajaya, supprimez-le lorsque votre application n'a plus besoin d'utiliser l'instance httpClient. Vous pourriez penser HttpClient clientqu'une telle suggestion semble vague, mais en fait, elle peut parfaitement s'aligner sur le cycle de vie de votre variable, ce qui est quelque chose de Programming-101 que vous faites probablement déjà de toute façon. Vous pourrez peut-être même encore l'utiliser using (...) {...}. Par exemple, consultez l'exemple Hello World dans ma réponse.
RayLuo
7

Comme il ne semble pas que quelqu'un l'ait encore mentionné ici, la nouvelle meilleure façon de gérer HttpClient et HttpClientHandler dans .Net Core 2.1 utilise HttpClientFactory .

Il résout la plupart des problèmes et des problèmes mentionnés ci-dessus d'une manière propre et facile à utiliser. De l'excellent article de blog de Steve Gordon :

Ajoutez les packages suivants à votre projet .Net Core (2.1.1 ou version ultérieure):

Microsoft.AspNetCore.All
Microsoft.Extensions.Http

Ajoutez ceci à Startup.cs:

services.AddHttpClient();

Injecter et utiliser:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public ValuesController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public async Task<ActionResult> Get()
    {
        var client = _httpClientFactory.CreateClient();
        var result = await client.GetStringAsync("http://www.google.com");
        return Ok(result);
    }
}

Explorez la série de publications dans le blog de Steve pour de nombreuses autres fonctionnalités.

pcdev
la source
4

Dans mon cas, je créais un HttpClient à l'intérieur d'une méthode qui a effectivement fait l'appel de service. Quelque chose comme:

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

Dans un rôle de travailleur Azure, après avoir appelé à plusieurs reprises cette méthode (sans supprimer le HttpClient), elle échouerait finalement avec SocketException(la tentative de connexion a échoué).

J'ai fait de HttpClient une variable d'instance (en la supprimant au niveau de la classe) et le problème a disparu. Donc, je dirais que oui, éliminez le HttpClient, en supposant qu'il est sûr (vous n'avez pas d'appels asynchrones en attente) pour le faire.

David Faivre
la source
Merci pour les commentaires. C'est une question quelque peu complexe. Je recommande de lire les articles liés dans la réponse de DPeden. En bref, l'instance HttpClient doit être réutilisée tout au long du cycle de vie de l'application. Si vous créez de nouvelles instances à plusieurs reprises, vous devrez peut-être les supprimer.
Fernando Correia
6
«l'instance HttpClient doit être réutilisée tout au long du cycle de vie de l'application», ce n'est pas une bonne idée avec beaucoup d'applications. Je pense aux applications Web qui utilisent HttpClient. HttpClient détient l'état (par exemple les en-têtes de demande qu'il utilisera), donc un thread de demande Web pourrait facilement piétiner ce que fait un autre. Dans les grandes applications Web, j'ai également vu HttpClient comme le problème des problèmes de connexion majeurs. En cas de doute, je dis Dispose.
bytedev
@nashwan vous ne pouvez pas effacer les en-têtes et en ajouter de nouveaux avant chaque demande?
Mandeep Janjua
Microsoft recommande également de réutiliser les instances HttpClient
Mandeep Janjua
@MandeepJanjua, cet exemple semble être un client en tant qu'application console. Je faisais allusion à une application Web étant le client.
bytedev
3

Dans une utilisation typique (réponses <2 Go), il n'est pas nécessaire de supprimer les HttpResponseMessages.

Les types de retour des méthodes HttpClient doivent être supprimés si leur contenu de flux n'est pas entièrement lu. Sinon, le CLR n'a aucun moyen de savoir que ces flux peuvent être fermés jusqu'à ce qu'ils soient récupérés.

  • Si vous lisez les données dans un octet [] (par exemple GetByteArrayAsync) ou une chaîne, toutes les données sont lues, il n'est donc pas nécessaire de les supprimer.
  • Les autres surcharges seront par défaut à la lecture du flux jusqu'à 2 Go (HttpCompletionOption est ResponseContentRead, HttpClient.MaxResponseContentBufferSize par défaut est 2 Go)

Si vous définissez HttpCompletionOption sur ResponseHeadersRead ou si la réponse est supérieure à 2 Go, vous devez nettoyer. Cela peut être fait en appelant Dispose sur le HttpResponseMessage ou en appelant Dispose / Close sur le flux obtenu à partir du contenu HttpResonseMessage ou en lisant le contenu complètement.

Que vous appeliez Dispose sur HttpClient dépend de si vous souhaitez annuler les demandes en attente ou non.

Tom Deseyn
la source
2

Si vous souhaitez supprimer HttpClient, vous pouvez le faire si vous le configurez en tant que pool de ressources. Et à la fin de votre candidature, vous disposez de votre pool de ressources.

Code:

// 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;
    }
}

var handler = HttpClientHander.GetHttpClientHandle (new Uri ("base url")).

  • HttpClient, en tant qu'interface, ne peut pas appeler Dispose ().
  • Dispose () sera appelé de manière différée par le garbage collector. Ou lorsque le programme nettoie l'objet via son destructeur.
  • Utilise des références faibles + une logique de nettoyage différé pour qu'il reste utilisé tant qu'il est fréquemment réutilisé.
  • Il alloue uniquement un nouveau HttpClient pour chaque URL de base qui lui est transmise. Les raisons expliquées par Ohad Schneider répondent ci-dessous. Mauvais comportement lors du changement d'URL de base.
  • HttpClientHandle permet de se moquer des tests
TamusJRoyce
la source
Parfait. Je vois que vous appelez la Disposeméthode que vous enregistrez sur GC. Cela devrait être noté plus haut sur le dessus.
Jeson Martajaya
Notez que HttpClient effectue le regroupement de ressources par URL de base. Donc, si vous consultez des milliers de sites Web différents dans une liste, vos performances se dégradent sans nettoyer ces sites individuels. Cela expose la possibilité de disposer de chaque URL de base. Cependant, si vous n'utilisez qu'un seul site Web, il se peut que ce soit uniquement pour des raisons académiques que vous appeliez Dispose.
TamusJRoyce
1

L'utilisation de l'injection de dépendances dans votre constructeur facilite la gestion de la durée de vie de votre HttpClient- en prenant la gestion de la vie en dehors du code qui en a besoin et en la rendant facilement modifiable à une date ultérieure.

Ma préférence actuelle est de créer une classe client http distincte qui hérite d' HttpClientune fois par domaine de point de terminaison cible , puis d'en faire un singleton à l'aide de l'injection de dépendances.public class ExampleHttpClient : HttpClient { ... }

Ensuite, je prends une dépendance de constructeur sur le client http personnalisé dans les classes de service où j'ai besoin d'accéder à cette API. Cela résout le problème de la durée de vie et présente des avantages en matière de regroupement de connexions.

Vous pouvez voir un exemple fonctionnel dans la réponse associée à https://stackoverflow.com/a/50238944/3140853

alastairtree
la source
-2

Je pense que l'on devrait utiliser un motif singleton pour éviter d'avoir à créer des instances de HttpClient et à le fermer tout le temps. Si vous utilisez .Net 4.0, vous pouvez utiliser un exemple de code comme ci-dessous. pour plus d'informations sur le motif singleton, cliquez ici .

class HttpClientSingletonWrapper : HttpClient
{
    private static readonly Lazy<HttpClientSingletonWrapper> Lazy= new Lazy<HttpClientSingletonWrapper>(()=>new HttpClientSingletonWrapper()); 

    public static HttpClientSingletonWrapper Instance {get { return Lazy.Value; }}

    private HttpClientSingletonWrapper()
    {
    }
}

Utilisez le code ci-dessous.

var client = HttpClientSingletonWrapper.Instance;
yayadavid
la source
3
Quelque chose à surveiller lors de cette opération (et d'autres schémas similaires): " Les membres d'instance ne sont pas garantis pour être thread-safe. "
tne
2
Que cette réponse soit correcte ou non devrait dépendre totalement de l'application à partir de laquelle vous souhaitez utiliser HttpClient. Si vous avez une application Web et créez un HttpClient singleton que toutes les demandes Web partageraient alors, vous obtiendrez potentiellement de nombreuses exceptions de connexion (selon la popularité de votre site Web! :-)). (Voir la réponse de David Faivre)
bytedev