Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

connexions tcp sur linux et forte charge

3 réponses
Avatar
Jean-Francois Smigielski
Bonsoir,

Je rencontre actuellement des problemes de fermetures de connexions tcp
sur un Linux.

Pour le mettre en evidence, j'ai ecrit un petit serveur d'echo qui
accepte plusieurs milliers de connexions par processus, et un client
capable de maintenir plusieurs milliers de connexions simultanees. Les
serveurs ne font que repondre aux sollicitations des clients. Ceux-ci
ouvrent des connexions vers les serveurs, font un peu de trafic,
attendent, ferment les connexions, et les rouvrent, etc...

Toutes les sockets sont non-bloquantes (pooling via un appel a select).
Tous les processus sont mono-threades.

Pour etre capable d'atteindre un nombre appreciable de connexions sur les
processus client, j'ai du positionner l'option SO_LINGER pour eviter
d'accumuler les connexions dans l'etat TIME_WAIT.

Sur les clients, j'ai pu remarquer que si le time_out passe a l'option
SO_LINGER est trop court, et qu'il y a plus de 4000 connexions etblies,
alors un arret brutal des processus clients (SIGTERM ou SIGQUIT) provoque
la fermeture (paquet TCP RST) d'une partie seulement des connexions.
C'est particulierement problematique car le serveur n'ayant aucune
nouvelle, il considere que ces connexions sont toujours dans leur etat
ESTABLISHED.

Une trace des communications montre explicitement que les paquets RST ne
sont pas emis du tout de la machine qui portait les clients. Il n'y a
aucune trace d'un quelconque message kernel qui indique une erreur.

Plus le time-out de SO_LINGER est grand, plus l'OS arrive a fermer une
grande partie des connexions etablies avec des paquets TCP FIN (celles
qu'il peut clore dans les temps), plus il peut en remettre a zero
(celles hors delai, via un paquet TCP RST), et plus le nombre de
connexions mortes sur le serveur est petit.

L'utilisation du mecanisme de tcp_keepalive permet de se rendre compte
des coupures mal detectees, mais les valeurs par defaut (2h avant de s'y
mettre) sont trop longues pour etre utilisables.

Avez-vous deja pu remarquer des comportements semblables?
Sont-ils connus? Ont-ils des correctifs?

Merci beaucoup,

Jean-Francois SMIGIELSKI

PS: mon mailer a quelques soucis avec les accents, ce qui explique leur
absence dans mon message. Que vive l'UTF-8!

3 réponses

Avatar
Xavier Roche
Jean-Francois Smigielski wrote:
Pour etre capable d'atteindre un nombre appreciable de connexions sur les
processus client, j'ai du positionner l'option SO_LINGER pour eviter
d'accumuler les connexions dans l'etat TIME_WAIT.


Question stupide: si vous n'utilisez pas cette option, que se passe-t-il
? (avec SO_REUSEADDR sur bind() evidemment)

Sur les clients, j'ai pu remarquer que si le time_out passe a l'option
SO_LINGER est trop court, et qu'il y a plus de 4000 connexions etblies,
alors un arret brutal des processus clients (SIGTERM ou SIGQUIT) provoque
la fermeture (paquet TCP RST) d'une partie seulement des connexions.


Hmm, sur un kernel récent ? C'est un comportement assez étonnant..

C'est particulierement problematique car le serveur n'ayant aucune
nouvelle, il considere que ces connexions sont toujours dans leur etat
ESTABLISHED.


Encore une suggestion stupide: un sigaction() qui trappe les
SIGTERM/etc., et qui referme proprement les sockets, en dernier recours ?

L'utilisation du mecanisme de tcp_keepalive permet de se rendre compte
des coupures mal detectees, mais les valeurs par defaut (2h avant de s'y
mettre) sont trop longues pour etre utilisables.


Et TCP keepalive semble déconseillé un peu partout.

Avatar
Jean-Francois Smigielski
On 2007-03-07, Xavier Roche <XXXXXXXXXXXXXXXXXXXXXXXXXXXXX> wrote:
Jean-Francois Smigielski wrote:
Pour etre capable d'atteindre un nombre appreciable de connexions sur les
processus client, j'ai du positionner l'option SO_LINGER pour eviter
d'accumuler les connexions dans l'etat TIME_WAIT.


Question stupide: si vous n'utilisez pas cette option, que se passe-t-il
? (avec SO_REUSEADDR sur bind() evidemment)


La question n'est pas stupide du tout. Si l'option SO_LINGER n'est pas
positionnee, les sockets qui se referment restent pres de 2 minutes dans
l'etat TIME_WAIT. Comme les clients tentent d'ouvri rbeaucoup de
connexions simultanees, il arrive un moment ou cela n'est plus possible,
faute de file_descriptors.


Sur les clients, j'ai pu remarquer que si le time_out passe a l'option
SO_LINGER est trop court, et qu'il y a plus de 4000 connexions etblies,
alors un arret brutal des processus clients (SIGTERM ou SIGQUIT) provoque
la fermeture (paquet TCP RST) d'une partie seulement des connexions.


Hmm, sur un kernel récent ? C'est un comportement assez étonnant..


Le kernel est "relativement" recent, il s'agit d'un Linux 2.6.18 compile
sans option particuliere.


C'est particulierement problematique car le serveur n'ayant aucune
nouvelle, il considere que ces connexions sont toujours dans leur etat
ESTABLISHED.


Encore une suggestion stupide: un sigaction() qui trappe les
SIGTERM/etc., et qui referme proprement les sockets, en dernier recours ?


Tout a fait, et c'est comme cela que j'ai du proceder. Mais le probleme
persiste encore : un appel a close sur les sockets qui utilisent l'option
SO_LINGER donne le meme resultat que l'arret du processus.


L'utilisation du mecanisme de tcp_keepalive permet de se rendre compte
des coupures mal detectees, mais les valeurs par defaut (2h avant de s'y
mettre) sont trop longues pour etre utilisables.


Et TCP keepalive semble déconseillé un peu partout.


Oui. le tcp keepalive n'est pas une operation anodine en terme de charge
CPU. Pour detecter rapidement les connexions mortes, j'ai rabaisse le
delai de 2 heures a 1 minute.
Sur une machine equipee de 2 processeurs Intel Xeon cadences a 3GHz, ca
a pris 15% du CPU pendant plusieurs secondes pour refermer toutes les
connexions mortes (a peu pres 17000).

JF Smigielski.


Avatar
Xavier Roche
Jean-Francois Smigielski wrote:
l'etat TIME_WAIT. Comme les clients tentent d'ouvri rbeaucoup de
connexions simultanees, il arrive un moment ou cela n'est plus possible,
faute de file_descriptors.


Hmm, cela ne devrait pas ? Il y a juste une paire locale/remote côté
kernel pour finir le nettoyage, mais cela consomme vraiment un fd ?

Tout a fait, et c'est comme cela que j'ai du proceder. Mais le probleme
persiste encore : un appel a close sur les sockets qui utilisent l'option
SO_LINGER donne le meme resultat que l'arret du processus.


Damned. Vous êtes en socket non bloquante ? close() ne renvoi pas EINTR
par hasard ? [le close() peut être bloquant..]