Augmenter le nombre maximal de connexions TCP / IP sous Linux

214

Je programme un serveur et il semble que mon nombre de connexions soit limité car ma bande passante n'est pas saturée même lorsque j'ai défini le nombre de connexions sur "illimité".

Comment puis-je augmenter ou éliminer un nombre maximal de connexions que ma boîte Ubuntu Linux peut ouvrir à la fois? Le système d'exploitation limite-t-il cela, ou est-ce le routeur ou le FAI? Ou s'agit-il d'autre chose?

red0ct
la source
2
@Software Monkey: J'ai quand même répondu à cette question car j'espère que cela pourrait être utile à quelqu'un qui écrit actuellement un serveur à l'avenir.
derobert
1
@derobert: J'ai vu ça +1. En fait, j'ai eu la même pensée après mon commentaire précédent, mais j'ai pensé que je laisserais le commentaire.
Lawrence Dol

Réponses:

396

Le nombre maximum de connexions est affecté par certaines limites à la fois côté client et côté serveur, quoique un peu différemment.

Côté client: augmentez la plage de ports sphériques et diminuez latcp_fin_timeout

Pour connaître les valeurs par défaut:

sysctl net.ipv4.ip_local_port_range
sysctl net.ipv4.tcp_fin_timeout

La plage de ports éphermaux définit le nombre maximal de sockets sortants qu'un hôte peut créer à partir d'une adresse IP particulière. Le fin_timeoutdéfinit la durée minimale pendant laquelle ces sockets resteront en TIME_WAITétat (inutilisables après avoir été utilisées une fois). Les valeurs par défaut du système habituelles sont:

  • net.ipv4.ip_local_port_range = 32768 61000
  • net.ipv4.tcp_fin_timeout = 60

Cela signifie essentiellement que votre système ne peut pas toujours garantir plus de (61000 - 32768) / 60 = 470sockets par seconde. Si vous n'êtes pas satisfait de cela, vous pouvez commencer par augmenter le port_range. Régler la plage sur 15000 61000est assez courant de nos jours. Vous pouvez augmenter encore la disponibilité en diminuant le fin_timeout. Supposons que vous fassiez les deux, vous devriez voir plus de 1 500 connexions sortantes par seconde, plus facilement.

Pour modifier les valeurs :

sysctl net.ipv4.ip_local_port_range="15000 61000"
sysctl net.ipv4.tcp_fin_timeout=30

Ce qui précède ne doit pas être interprété comme les facteurs affectant la capacité du système à établir des connexions sortantes par seconde. Mais ces facteurs affectent plutôt la capacité du système à gérer les connexions simultanées de manière durable pendant de longues périodes «d'activité».

Les valeurs par défaut de Sysctl sur une boîte Linux typique pour tcp_tw_recycle& tcp_tw_reuseseraient

net.ipv4.tcp_tw_recycle=0
net.ipv4.tcp_tw_reuse=0

Celles-ci n'autorisent pas une connexion à partir d'un socket "utilisé" (en attente) et forcent les sockets à durer le time_waitcycle complet . Je recommande de régler:

sysctl net.ipv4.tcp_tw_recycle=1
sysctl net.ipv4.tcp_tw_reuse=1 

Cela permet un cycle rapide des sockets en time_waitétat et de les réutiliser. Mais avant d'effectuer cette modification, assurez-vous que cela n'entre pas en conflit avec les protocoles que vous utiliseriez pour l'application qui a besoin de ces sockets. Assurez-vous de lire le post "Coping with the TCP TIME-WAIT" de Vincent Bernat pour comprendre les implications. L' net.ipv4.tcp_tw_recycle option est assez problématique pour les serveurs publics car elle ne gère pas les connexions de deux ordinateurs différents derrière le même périphérique NAT , ce qui est difficile à détecter et à attendre pour vous mordre. Notez que cela net.ipv4.tcp_tw_recyclea été supprimé de Linux 4.12.

Côté serveur: la net.core.somaxconnvaleur a un rôle important. Il limite le nombre maximal de requêtes mises en file d'attente sur un socket d'écoute. Si vous êtes sûr de la capacité de votre application serveur, augmentez-la de 128 par défaut à quelque chose comme 128 à 1024. Vous pouvez maintenant profiter de cette augmentation en modifiant la variable d'écoute de backlog dans l'appel d'écoute de votre application, à un entier égal ou supérieur.

sysctl net.core.somaxconn=1024

txqueuelenLes paramètres de vos cartes Ethernet ont également un rôle à jouer. Les valeurs par défaut sont 1000, alors augmentez-les jusqu'à 5000 ou même plus si votre système peut les gérer.

ifconfig eth0 txqueuelen 5000
echo "/sbin/ifconfig eth0 txqueuelen 5000" >> /etc/rc.local

De même, augmentez les valeurs de net.core.netdev_max_backloget net.ipv4.tcp_max_syn_backlog. Leurs valeurs par défaut sont respectivement 1000 et 1024.

sysctl net.core.netdev_max_backlog=2000
sysctl net.ipv4.tcp_max_syn_backlog=2048

N'oubliez pas de démarrer vos applications côté client et côté serveur en augmentant les ulimts FD, dans le shell.

Outre la technique ci-dessus, la plus utilisée par les programmeurs consiste à réduire le nombre d' appels d' écriture TCP . Ma propre préférence est d'utiliser un tampon dans lequel je pousse les données que je souhaite envoyer au client, puis aux points appropriés j'écris les données tamponnées dans le socket réel. Cette technique me permet d'utiliser de gros paquets de données, de réduire la fragmentation, de réduire l'utilisation de mon processeur à la fois dans le pays utilisateur et au niveau du noyau.

mdk
la source
4
Réponse brillante! Mon problème était un peu différent, c'est-à-dire que j'essayais de déplacer les informations de session d'un stockage de session de niveau application vers redis via PHP. Pour une raison quelconque, je ne pouvais pas ajouter plus de 28230 sessions sans ajouter beaucoup de sommeil en une seule fois, sans aucune erreur ni en php ni en redis logs. Nous avons cassé nos têtes à ce sujet pendant une journée entière jusqu'à ce que je pense que le problème n'est peut-être pas avec php / redis mais dans la couche tcp / ip reliant les deux et sommes arrivés à cette réponse. A réussi à résoudre le problème en peu de temps après cela :) Merci beaucoup!
s1d
27
N'oubliez pas que nous parlons toujours de port IP +. Vous pouvez avoir des sockets «illimitées» ouvertes sur le port XY à partir de nombreuses adresses IP différentes. La limite de 470 s'applique uniquement aux sockets ouvertes simultanées sur la même IP. Un autre IP peut avoir ses propres 470 connexions aux mêmes ports.
Marki555
6
@ Marki555: Votre commentaire est TRÈS CORRECT. Les applications développées pour générer et maintenir un grand nombre de connexions sortantes, doivent avoir une "connaissance" des adresses IP disponibles pour créer des connexions sortantes, et doivent ensuite se lier de manière appropriée à ces adresses IP en utilisant une sorte d '"algorithme à tour de rôle", et maintenir un "tableau de bord".
mdk
8
Cette réponse a des erreurs. Tout d'abord, net.ipv4.tcp_fin_timeout est uniquement pour l'état FIN_WAIT_2 ( cs.uwaterloo.ca/~brecht/servers/ip-sysctl.txt ). Deuxièmement, comme @Eric l'a dit, "470 sockets à un moment donné" n'est pas correct.
Sharvanath
3
@mdk: Je ne suis pas clair avec cette partie de calcul (61000 - 32768) / 60 = 470 sockets per second. Pouvez-vous développer cela?
Tom Taylor
64

Il existe quelques variables pour définir le nombre maximal de connexions. Très probablement, vous manquez de numéros de fichier en premier. Vérifiez ulimit -n. Après cela, il y a des paramètres dans / proc, mais ceux par défaut sont des dizaines de milliers.

Plus important encore, il semble que vous faites quelque chose de mal. Une seule connexion TCP doit pouvoir utiliser toute la bande passante entre deux parties; si ce n'est pas le cas:

  • Vérifiez si votre paramètre de fenêtre TCP est suffisamment grand. Les valeurs par défaut de Linux sont bonnes pour tout sauf les liaisons inet très rapides (centaines de Mbps) ou les liaisons satellites rapides. Quel est votre produit de retard de bande passante *?
  • Vérifiez la perte de paquets en utilisant ping avec de gros paquets ( ping -s 1472...)
  • Vérifiez la limitation de débit. Sous Linux, ceci est configuré avectc
  • Confirmez que la bande passante que vous pensez exister existe réellement en utilisant, par exemple, iperf
  • Confirmez que votre protocole est sain d'esprit. Rappelez-vous la latence.
  • S'il s'agit d'un gigabit + LAN, pouvez-vous utiliser des paquets jumbo? Es-tu?

J'ai peut-être mal compris. Peut-être que vous faites quelque chose comme Bittorrent, où vous avez besoin de beaucoup de connexions. Si tel est le cas, vous devez déterminer le nombre de connexions que vous utilisez réellement (essayez netstatou lsof). Si ce nombre est important, vous pourriez:

  • Avoir beaucoup de bande passante, par exemple, 100 Mbps +. Dans ce cas, il se peut que vous ayez besoin de monter le fichier ulimit -n. Pourtant, ~ 1000 connexions (par défaut sur mon système) sont assez nombreuses.
  • Vous avez des problèmes de réseau qui ralentissent vos connexions (par exemple, la perte de paquets)
  • Quelque chose d'autre vous ralentit, par exemple la bande passante d'E / S, surtout si vous recherchez. Avez-vous vérifié iostat -x?

De plus, si vous utilisez un routeur NAT de qualité grand public (Linksys, Netgear, DLink, etc.), sachez que vous pouvez dépasser ses capacités avec des milliers de connexions.

J'espère que cela vous aidera. Vous posez vraiment une question de réseautage.

derobert
la source
16

Pour améliorer la réponse donnée par derobert,

Vous pouvez déterminer la limite de connexion de votre système d'exploitation en attachant nf_conntrack_max.

Par exemple: cat / proc / sys / net / netfilter / nf_conntrack_max

Vous pouvez utiliser le script suivant pour compter le nombre de connexions TCP vers une plage donnée de ports TCP. Par défaut 1-65535.

Cela confirmera si vous maximisez ou non votre limite de connexion au système d'exploitation.

Voici le script.

#!/bin/bash
OS=$(uname)

case "$OS" in
    'SunOS')
            AWK=/usr/bin/nawk
            ;;
    'Linux')
            AWK=/bin/awk
            ;;
    'AIX')
            AWK=/usr/bin/awk
            ;;
esac

netstat -an | $AWK -v start=1 -v end=65535 ' $NF ~ /TIME_WAIT|ESTABLISHED/ && $4 !~ /127\.0\.0\.1/ {
    if ($1 ~ /\./)
            {sip=$1}
    else {sip=$4}

    if ( sip ~ /:/ )
            {d=2}
    else {d=5}

    split( sip, a, /:|\./ )

    if ( a[d] >= start && a[d] <= end ) {
            ++connections;
            }
    }
    END {print connections}'
whitehat237
la source
3
which awkest votre ami pour déterminer le chemin vers awk, SunOS y a aussi un lien :)
Panagiotis Moustafellos
2
@PanagiotisM. whichrepose sur le programme pour être dans PATHce cas, vous pouvez simplement utiliser awkau lieu de fournir le chemin d'accès complet. (Cela dit, je ne sais pas si la solution du script est plus proche de la perfection, mais ce n'est pas de cela qu'il s'agit).
Michael Krelin - pirate
5
J'adore la façon dont ce script va balistique pour déterminer l' awkemplacement, mais suppose que le shell est toujours /bin/bash (astuce pro: AIX5 / 6 n'a même pas bash par défaut).
kubanczyk
La awkdétection est-elle utile? Personnellement, je supposerais simplement d'avoir une PATHalternative correcte mais raisonnable pourrait être /usr/bin/env awket /usr/bin/env bashrespectivement. Pour ce que ça vaut, il s'est trompé de position sur mon système Linux. Ce n'est /usr/bin/awkpas/bin/awk
Wolph
1
quand j'exécute ce script, j'obtiens 798, alors qu'est-ce que cela signifie?
10

Au niveau d'une application, voici quelque chose qu'un développeur peut faire:

Côté serveur:

  1. Vérifiez si l'équilibreur de charge (le cas échéant) fonctionne correctement.

  2. Transformez les délais d'attente TCP lents en réponse 503 rapide immédiate, si vous chargez correctement l'équilibreur de charge, il devrait choisir la ressource de travail à servir, et c'est mieux que de rester là avec des massages d'erreur inattendus.

Par exemple: si vous utilisez un serveur de nœuds, vous pouvez utiliser toobusy à partir de npm. Mise en œuvre quelque chose comme:

var toobusy = require('toobusy');
app.use(function(req, res, next) {
  if (toobusy()) res.send(503, "I'm busy right now, sorry.");
  else next();
});

Pourquoi 503? Voici quelques bonnes informations sur la surcharge: http://ferd.ca/queues-don-t-fix-overload.html

Nous pouvons aussi faire un peu de travail côté client:

  1. Essayez de regrouper les appels par lots, de réduire le trafic et le nombre total de demandes b / w client et serveur.

  2. Essayez de créer une couche intermédiaire de cache pour gérer les demandes de doublons inutiles.

Kev
la source