récemment, je suis tombé sur ce post de blog de monstres asp.net qui parle de problèmes liés à l'utilisation HttpClient
de la manière suivante:
using(var client = new HttpClient())
{
}
Selon l'article du blog, si nous disposons HttpClient
après chaque demande, les connexions TCP peuvent rester ouvertes. Cela peut potentiellement conduire à System.Net.Sockets.SocketException
.
La bonne manière, comme dans le post, est de créer une instance unique HttpClient
car cela aide à réduire le gaspillage de sockets.
De la poste:
Si nous partageons une seule instance de HttpClient, nous pouvons réduire le gaspillage de sockets en les réutilisant:
namespace ConsoleApplication { public class Program { private static HttpClient Client = new HttpClient(); public static void Main(string[] args) { Console.WriteLine("Starting connections"); for(int i = 0; i<10; i++) { var result = Client.GetAsync("http://aspnetmonsters.com").Result; Console.WriteLine(result.StatusCode); } Console.WriteLine("Connections done"); Console.ReadLine(); } } }
J'ai toujours disposé d'un HttpClient
objet après l'avoir utilisé car je sentais que c'était la meilleure façon de l'utiliser. Mais cet article de blog me fait maintenant sentir que je le faisais mal tout ce temps.
Devrions-nous créer une nouvelle instance unique HttpClient
pour toutes les demandes? Y a-t-il des pièges à utiliser une instance statique?
la source
Close()
ou lancer un nouveauGet()
. Si vous ne vous débarrassez que du client une fois que vous en avez terminé, il n'y aura plus personne pour gérer cette poignée de main de fermeture et vos ports auront tous l'état TIME_WAIT, à cause de cela.Réponses:
Cela semble être un article de blog convaincant. Cependant, avant de prendre une décision, je commencerais par exécuter les mêmes tests que l'auteur du blog, mais sur votre propre code. Je voudrais aussi essayer d'en savoir un peu plus sur HttpClient et son comportement.
Ce post dit:
Donc, ce qui se passe probablement lorsqu'un HttpClient est partagé, c'est que les connexions sont réutilisées, ce qui est bien si vous n'avez pas besoin de connexions persistantes. La seule façon pour vous de savoir avec certitude si cela est important pour votre situation est d'exécuter vos propres tests de performance.
Si vous creusez, vous trouverez plusieurs autres ressources qui traitent de ce problème (y compris un article Microsoft Best Practices), il est donc probablement préférable de l'implémenter de toute façon (avec quelques précautions).
Références
Vous utilisez mal le client et il déstabilise votre logiciel
Singleton HttpClient? Méfiez-vous de ce comportement sérieux et du moyen de le corriger.
Modèles et pratiques Microsoft - Optimisation des performances: instanciation incorrecte
Instance unique de HttpClient réutilisable lors de la révision du code
Singleton HttpClient ne respecte pas les modifications DNS (CoreFX)
Conseils généraux d'utilisation de HttpClient
la source
Je suis en retard à la fête, mais voici mon parcours d’apprentissage sur ce sujet épineux.
1. Où pouvons-nous trouver le défenseur officiel de la réutilisation de HttpClient?
Je veux dire, si la réutilisation de HttpClient est prévue et qu'il est important de le faire , un tel avocat est mieux documenté dans sa propre documentation d'API, plutôt que d'être caché dans de nombreux "Sujets avancés", "Performances (anti) modèles" ou autres blogs. . Sinon, comment un nouvel apprenant est-il censé le savoir avant qu'il ne soit trop tard?
Dès à présent (mai 2018), le premier résultat de recherche lorsque Google "c # httpclient" pointe vers cette page de référence d'API sur MSDN , ce qui ne mentionne absolument pas cette intention. Pour les débutants, la première leçon est de toujours cliquer sur le lien "Autres versions" juste après le titre de la page d’aide de MSDN. Vous y trouverez probablement des liens vers la "version actuelle". Dans ce cas HttpClient, cela vous mènera au dernier document contenant cette description d'intention .
J'imagine que de nombreux développeurs novices dans ce sujet n'ont pas trouvé la bonne page de documentation. C'est pourquoi ces connaissances ne sont pas très répandues et les gens ont été surpris d'apprendre plus tard , peut-être de manière difficile .
2. La (mis?) Conception de
using
IDisposable
Celui-ci est un peu hors sujet, mais mérite néanmoins d'être souligné, ce n'est pas un hasard si les personnes dans les articles de blog susmentionnés blâment comment
HttpClient
leurIDisposable
interface les incite à utiliser leusing (var client = new HttpClient()) {...}
modèle, puis à conduire au problème.Je crois que cela revient à une conception non dite (mal?): "Un objet identifiable devrait être de courte durée" .
CEPENDANT, même si cela ressemble certainement à une chose de courte durée lorsque nous écrivons du code dans ce style:
la documentation officielle sur IDisposable ne mentionne jamais que les
IDisposable
objets doivent être de courte durée. Par définition, IDisposable est simplement un mécanisme vous permettant de libérer des ressources non gérées. Rien de plus. En ce sens, vous êtes censé éventuellement déclencher la cession, mais cela ne vous oblige pas à le faire de manière éphémère.Il est donc de votre devoir de bien choisir quand déclencher la mise au rebut, en vous basant sur les exigences du cycle de vie de votre objet réel. Rien ne vous empêche d’utiliser un IDisposable de manière durable:
Avec cette nouvelle compréhension, maintenant nous revisitons cet article de blog , nous pouvons clairement remarquer que le "correctif" s'initialise
HttpClient
une 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 PAS été correctement fermé. S'il était fermé, son état serait plutôt dans TIME_WAIT. Dans la pratique, il n’est pas difficile de laisser filtrer une seule connexion ouverte après la fin de votre programme et l’affiche du blog continue d’obtenir un gain de performances après le correctif; mais toujours, il est conceptuellement incorrect de blâmer IDisposable et de choisir de ne PAS le jeter.3. Devons-nous mettre HttpClient dans une propriété statique, ou même le mettre comme singleton?
Sur la base de la compréhension de la section précédente, je pense que la réponse devient claire: "pas nécessairement". Cela dépend vraiment de la manière dont vous organisez votre code, tant que vous réutilisez un HttpClient ET (idéalement) le supprimez éventuellement.
De manière hilarante, même l'exemple de la section Remarques du document officiel actuel ne le fait pas parfaitement. Il définit une classe "GoodController", contenant une propriété statique HttpClient qui ne sera pas supprimée. qui désobéit à ce qu'un autre exemple de la section Exemples souligne: "il faut appeler Dispose ... pour que l'application ne fuit pas de ressources".
Enfin, singleton n’est pas sans défis.
- Cité de cette conférence inspirante, "Global State and Singletons"
PS: SqlConnection
Celui-ci est sans rapport avec l'actuel Q & A, mais c'est probablement un bon à savoir. Le modèle d'utilisation de SqlConnection est différent. Vous n'avez PAS besoin de réutiliser SqlConnection , car il gérera mieux son pool de connexions.
La différence est causée par leur approche de mise en œuvre. Chaque instance HttpClient utilise son propre pool de connexions (cité ci- dessous ); mais SqlConnection lui-même est géré par un pool de connexion central, selon cela .
Et vous devez toujours disposer de SqlConnection, comme vous êtes supposé le faire pour HttpClient.
la source
J'ai fait quelques tests, voir des améliorations de performances avec statique
HttpClient
. J'ai utilisé le code ci-dessous pour mes tests:Pour tester:
J'ai trouvé l'amélioration des performances entre 40% et 60% en utilisant statique
HttpClient
au lieu de la jeter à laHttpClient
demande. J'ai mis les détails du résultat du test de performance dans l'article du blog ici .la source
Pour fermer correctement la connexion TCP , nous devons effectuer une séquence de paquets FIN-FIN + ACK-ACK (tout comme SYN - SYN + ACK - ACK, lors de l’ ouverture d’une connexion TCP ). Si nous appelons simplement une méthode .Close () (ce qui se produit généralement lorsqu'un HttpClient est en train de se départir) et si nous n'attendons pas que le côté distant confirme notre demande de fermeture (avec FIN + ACK), nous nous retrouvons avec l'état TIME_WAIT sur le port TCP local, car nous avons jeté notre écouteur (HttpClient) et nous n’avons jamais eu la possibilité de réinitialiser l’état du port à un état fermé approprié, une fois que l’homologue distant nous a envoyé le paquet FIN + ACK.
La bonne façon de fermer la connexion TCP serait d'appeler la méthode .Close () et d'attendre que l'événement de fermeture de l'autre côté (FIN + ACK) arrive de notre côté. Ce n’est qu’alors que nous pourrons envoyer notre ACK final et disposer du HttpClient.
Juste pour ajouter, il est logique de garder les connexions TCP ouvertes, si vous exécutez des requêtes HTTP, à cause de l'en-tête HTTP "Connexion: Keep-Alive". De plus, vous pouvez demander à l'homologue distant de fermer la connexion pour vous en définissant l'en-tête HTTP "Connection: Close". De cette façon, vos ports locaux seront toujours correctement fermés, au lieu d'être dans un état TIME_WAIT.
la source
Voici un client API de base qui utilise efficacement HttpClient et HttpClientHandler. Lorsque vous créez un nouveau HttpClient pour faire une demande, il y a beaucoup de surcharge. Ne recréez pas HttpClient pour chaque demande. Réutilisez HttpClient autant que possible ...
Usage:
la source
Il n'y a pas qu'un seul moyen d'utiliser la classe HttpClient. L'important est de concevoir votre application de manière à tenir compte de son environnement et de ses contraintes.
HTTP est un excellent protocole à utiliser lorsque vous devez exposer des API publiques. Il peut également être utilisé efficacement pour les services internes légers à faible temps de latence - bien que le modèle de file d'attente de messages RPC constitue souvent un meilleur choix pour les services internes.
Bien gérer HTTP est une opération très complexe.
Considérer ce qui suit:
Mais surtout, testez, mesurez et confirmez. S'il ne se comporte pas comme prévu, nous pouvons alors répondre à des questions spécifiques sur la manière d'atteindre les résultats escomptés.
la source
HttpClient
implémenteIDisposable
. Il n’est donc pas déraisonnable de s’attendre à ce qu’il s’agisse d’un objet éphémère qui sait se nettoyer lui-même et qui se prête bien à l’enveloppement d’uneusing
déclaration à chaque fois que vous en avez besoin. Malheureusement, ce n'est pas comme ça que ça fonctionne. Le billet de blog lié au PO montre clairement qu'il existe des ressources (en particulier des connexions de socket TCP) qui subsistent longtemps après que lausing
déclaration est devenue hors de portée et que l'HttpClient
objet a probablement été supprimé.