C # - Configuration de la durée pendant laquelle une connexion HttpClient restera ouverte

C # - Configuration de la durée pendant laquelle une connexion HttpClient restera ouverte

Lorsque vous utilisez une seule instance de HttpClient pour envoyer des requêtes, elle maintient les connexions ouvertes afin d'accélérer les requêtes futures. Par défaut, les connexions inactives sont fermées après 2 minutes, sinon elles resteront ouvertes pour toujours (en théorie). En réalité, la connexion peut être fermée par le côté serveur (ou d'autres facteurs externes) quels que soient les paramètres côté client.

Il existe deux paramètres qui contrôlent la durée pendant laquelle une connexion sera maintenue ouverte. Vous pouvez les changer. Ils sont différents dans .NET Framework et .NET Core. Voici un tableau montrant les paramètres, leurs valeurs par défaut et les propriétés que vous pouvez définir pour les modifier :

Paramètre Par défaut .NET Framework .NET Core
Délai de connexion inactive 2 minutes ServicePoint.MaxIdleTime SocketsHttpHandler.PooledIdleConnectionTimeout
Durée de vie maximale de la connexion Pour toujours ServicePoint.ConnectionLeaseTimeout SocketsHttpHandler.PooledConnectionLifetime

Dans cet article, je vais montrer des exemples de modification de ces paramètres dans .NET Framework et .NET Core, comment fermer immédiatement une connexion et discuter de la configuration côté serveur.

Remarque :SocketsHttpHandler a été introduit dans .NET Core 2.1.

Surveillance des connexions

Vous pouvez utiliser netstat pour surveiller la connexion afin de voir les effets de la modification des paramètres. Au lieu de montrer les résultats netstat dans chaque section, je vais simplement discuter des résultats finaux de haut niveau.

J'exécute une API Web localement sur le port 9000 et j'établis des connexions dans une application de console exécutée localement. Voici à quoi ressemble netstat lorsqu'une connexion est ouverte :

C:\WINDOWS\system32>netstat -an | find "9000"
  TCP    127.0.0.1:9000         0.0.0.0:0              LISTENING
  TCP    [::1]:2867             [::1]:9000             ESTABLISHED
  TCP    [::1]:9000             [::]:0                 LISTENING
  TCP    [::1]:9000             [::1]:2867             ESTABLISHED
Code language: plaintext (plaintext)

Modification du délai d'expiration de la connexion inactive

Par défaut, une connexion inactive est fermée après 2 minutes. Si une connexion n'est pas actuellement utilisée pour envoyer une demande, elle est considérée comme inactive. Dans les exemples ci-dessous, je vais changer le délai de connexion inactive à 5 minutes.

Dans .NET Framework

Définissez ServicePoint.MaxIdleTime pour modifier le délai d'inactivité de la connexion :

//create the single instance
httpClient = new HttpClient();

var sp = ServicePointManager.FindServicePoint(new Uri("https://localhost:9000"));
sp.MaxIdleTime = (int)TimeSpan.FromMinutes(5).TotalMilliseconds;
Code language: C# (cs)

Remarque :Vous pouvez le définir au niveau de ServicePointManager si vous souhaitez l'appliquer à toutes les URL.

Dans .NET Core

Définissez SocketsHttpHandler.PooledConnectionIdleTimeout et transmettez le gestionnaire au HttpClient :

var socketsHttpHandler = new SocketsHttpHandler()
{
	PooledConnectionIdleTimeout = TimeSpan.FromMinutes(5),
};
httpClient = new HttpClient(socketsHttpHandler);
Code language: C# (cs)

Résultats

Voici le code pour envoyer une demande :

var response = await httpClient.GetAsync("https://localhost:9000/stocks/MSFT");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
Code language: C# (cs)

Lorsqu'une requête est envoyée, une connexion est ouverte. Une fois la demande effectuée, la connexion est inactive.

Le délai d'expiration de la connexion inactive est défini sur 5 minutes, il y a donc deux résultats possibles :

  • Une autre requête est envoyée avant 5 minutes. La connexion est toujours ouverte, elle sera donc réutilisée. Le minuteur d'inactivité sera réinitialisé.
  • Aucune demande n'est envoyée, la connexion reste donc inactive pendant 5 minutes et est fermée.

Comme vous pouvez le constater, une connexion peut être maintenue ouverte indéfiniment car le minuteur d'inactivité est réinitialisé chaque fois que la connexion est réutilisée.

Modifier la durée de vie maximale de la connexion

Par défaut, les connexions peuvent rester indéfiniment tant qu'elles sont utilisées. Si cela n'est pas souhaitable, vous pouvez le modifier. Dans les exemples ci-dessous, je limiterai la durée de vie de la connexion à 5 minutes. Les résultats sont différents entre .NET Framework et .NET Core.

Dans .NET Framework

Définissez ServicePoint.ConnectionLeaseTimeout pour modifier la durée de vie maximale de la connexion :

//create the single instance
httpClient = new HttpClient();

var sp = ServicePointManager.FindServicePoint(new Uri("https://localhost:9000"));
sp.ConnectionLeaseTimeout = (int)TimeSpan.FromMinutes(5).TotalMilliseconds;
Code language: C# (cs)

Cela fermera la connexion après 5 minutes, quelle que soit la durée d'inactivité. Il fermera même les connexions semi-fermées persistantes.

Contrairement à .NET Core, lorsque cela ferme une connexion, cela ne la laisse pas dans un état semi-fermé.

Dans .NET Core

Définissez SocketsHttpHandler.PooledConnectionLifetime pour modifier la durée de vie maximale de la connexion :

var socketHttpHandler = new SocketsHttpHandler()
{
	PooledConnectionLifetime = TimeSpan.FromMinutes(5),
};
httpClient = new HttpClient(socketHttpHandler);
Code language: C# (cs)

J'ai remarqué deux choses lors de l'utilisation :

  • Ce n'est pas précis. Il semble toujours fermer la connexion ~ 30 secondes après l'heure spécifiée. Ex :si je spécifie 1 minute, la connexion sera effectivement fermée après 1,5 minute. Remarque :Il peut s'agir d'une interrogation interne avec un intervalle codé en dur.
  • Il ferme la connexion de la même manière que les connexions inactives sont fermées. Il laisse la connexion dans un état semi-fermé, où elle s'attarde un peu. Remarque :Ceci est différent du comportement dans .NET Framework, où les connexions sont effacées.

Résultats

Un bon moyen de voir les effets de la définition de la durée de vie maximale de la connexion consiste à envoyer des connexions périodiquement :

while (true)
{
	var response = await httpClient.GetAsync("https://localhost:9000/stocks/MSFT");
	response.EnsureSuccessStatusCode();
	Console.WriteLine(await response.Content.ReadAsStringAsync());

	await Task.Delay(TimeSpan.FromMinutes(1));
}
Code language: C# (cs)

Cela envoie des demandes toutes les 1 minute. Lors de l'envoi de la première requête, une connexion est ouverte. Cela signifie que la connexion n'est jamais inactive pendant plus d'une minute.

Étant donné que la durée de vie maximale de la connexion est définie sur 5 minutes, la première connexion est fermée après 5 minutes et une nouvelle connexion est ouverte pour les requêtes suivantes.

Cela montre que la définition de la durée de vie maximale de la connexion limite la durée d'une connexion, quelle que soit son utilisation.

Lorsque vous ne souhaitez pas réutiliser les connexions

Les connexions HTTP sont persistantes par défaut (depuis HTTP 1.1) pour permettre la réutilisation. Comme il s'agit de la valeur par défaut, vous n'avez rien à faire de spécial pour activer ce comportement. Que faire si vous ne souhaitez pas réutiliser les connexions et souhaitez plutôt les fermer immédiatement ? Vous pouvez le faire en ajoutant la Connexion :fermer en-tête de requête.

Le moyen le plus simple de le faire avec HttpClient est de définir DefaultRequestHeaders.ConnectionClose =true.

httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.ConnectionClose = true;
Code language: C# (cs)

Cela ajoutera la Connexion :fermer en-tête à toutes les requêtes envoyées avec cette instance HttpClient. Chaque demande ouvrira une connexion et la fermera une fois terminée. Soyez prudent avec cela.

Cela fonctionne de la même manière dans .NET Framework et .NET Core.

Configuration côté serveur

Les connexions inactives peuvent être fermées par le serveur (ou tout autre facteur externe) quels que soient les paramètres côté client. Le paramètre qui contrôle cela est généralement appelé délai de maintien en vie (ce n'est pas la même chose que TCP KeepAlive). Cela contrôle la durée pendant laquelle une connexion inactive sera maintenue ouverte.

Le délai d'expiration par défaut de keep-alive varie selon les différents serveurs Web. Par exemple :

  • Dans Apache HTTP Server 2.4, le paramètre KeepAliveTimeout est défini par défaut sur 5 secondes.
  • Dans ASP.NET Core Kestrel, le paramètre Limits.KeepAliveTimeout est défini par défaut sur 2 minutes.
  • Dans nginx, le paramètre keepalive_timeout est par défaut de 75 secondes.

Les paramètres côté client et côté serveur affectent la durée d'ouverture d'une connexion inactive. Le côté qui a la valeur la plus basse déterminera quand la connexion inactive est fermée. Par exemple, si vous avez un délai d'attente de connexion inactive côté client de 5 minutes et qu'il est de 2 minutes côté serveur, une connexion inactive sera fermée par le serveur après 2 minutes.

Crécerelle du noyau ASP.NET

Voici un exemple de modification de la valeur du délai d'attente keep-alive dans ASP.NET Core lors de l'utilisation de Kestrel :

var builder = Host.CreateDefaultBuilder(args)
	.ConfigureWebHostDefaults(webBuilder =>
	{
		webBuilder.UseKestrel(options => 
		{
			options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(5);
		})
		.UseStartup<Startup>()
		.UseUrls(url)
	});
Code language: C# (cs)