...

Server Socket Buffers dans l'hébergement : optimiser le débit et la latence

Buffers de socket déterminent, dans l'hébergement, la quantité de données qu'une connexion TCP entre le serveur d'applications et le client met en mémoire tampon à court terme et la vitesse à laquelle les réponses arrivent. Je montre comment régler la taille des tampons de manière à augmenter le débit et à réduire la latence sans générer de coûts inutiles. RAM de gaspiller.

Points centraux

  • Taille de la mémoire tampon s'aligner sur la bande passante et le RTT
  • Pile TCP et Congestion Control
  • Mesure avec iperf/netperf avant toute modification
  • Paramètres du noyau augmenter progressivement
  • Sécurité via des limites de taux et des cookies SYN

Ce que font les socket buffers dans l'hébergement

Je vois les tampons de socket comme Envoyer- et des tampons de réception qui lissent les flux TCP et réduisent les retransmissions. Les petites mémoires tampons forcent le TCP à effectuer de fréquents accessions et segments, ce qui freine le débit et sollicite davantage le CPU. Les tampons trop grands consomment beaucoup Mémoire et peuvent retarder lescks, ce qui provoque des pics de latence. Dans les centres de calcul avec 10 Gbit/s ou plus, la norme ne suffit souvent pas, car la fenêtre TCP reste trop petite. Une fenêtre harmonisée permet des trains de données plus importants, ce qui accélère de manière mesurable les transferts de fichiers volumineux et les réponses API.

La bonne taille : formule et pratique

Je dimensionne les buffers avec la relation simple Bande passante × RTT ÷ 8 ; à 10 Gbit/s et 10 ms RTT, j'obtiens environ 12,5 Mo par direction. En pratique, je commence par une taille plus petite, environ 1-4 Mo, puis je vérifie pas à pas le débit et le RTT. Les valeurs exactes dépendent du chemin de latence, des pertes de paquets et de la charge de travail, c'est pourquoi je vérifie chaque modification avec des tests de charge. Pour les ajustements persistants du noyau, j'ai recours à sysctl et je garde la configuration proprement documentée, voir ma brève référence à Réglage Linux-Sysctl. C'est ainsi que je trouve le point où plus de mémoire tampon n'apporte aucun avantage supplémentaire et où je dois utiliser le Zone de frappe rencontre.

Piles TCP et contrôle de congestion

Je combine les Algorithmes CC avec des valeurs tampons judicieuses, car les deux déterminent ensemble le contrôle des fenêtres. TCP CUBIC s'harmonise souvent avec des latences DC typiques, tandis que BBR brille avec des RTT plus longs et une légère perte. Le Window Scaling utilise plus efficacement des buffers plus grands, à moins que l'application ne force elle-même de petits chunks. Ceux qui souhaitent comparer la pile de manière plus approfondie trouveront des informations de fond à ce sujet dans mon renvoi à Contrôle de la congestion TCP. Ce qui reste important : Je ne modifie jamais toutes les vis de réglage à la fois, afin de pouvoir évaluer l'influence de chaque vis. Paramètres propre.

Mesure : tester le débit et la latence

Sans mesure, je reste aveugle, c'est pourquoi j'utilise iperf, netperf et les logs de serveur pour TTFB, RTT et retransmissions. Je teste en mode inactif et sous charge réelle afin de détecter les bursts, la mise en file d'attente et la gigue. Les RTT plus courts se manifestent rapidement lorsque les buffers ne retiennent pas artificiellement lescks et que la segmentation diminue. En plus du réseau, je mesure le CPU, la charge IRQ et les changements de contexte, car les goulots d'étranglement proviennent rarement des buffers seuls. Une comparaison propre avant/après réduit les devinettes et permet d'économiser beaucoup au final. Temps.

Paramètres et valeurs recommandés pour le noyau

Je commence par des plafonds modérés pour rmem et wmem, puis j'augmente en fonction des besoins et je surveille la consommation de mémoire. Pour ce faire, je place généralement net.core.rmem_max et wmem_max dans la plage des deux chiffres en Mo, tandis que tcp_rmem/wmem contrôle les valeurs min/défaut/max dynamiques. Somaxconn augmente la file d'attente du backlog et empêche les rejets lors des vagues de connexion. J'écris toutes les modifications dans /etc/sysctl.conf et je le recharge de manière contrôlée afin de pouvoir revenir en arrière à tout moment. Le tableau suivant regroupe les valeurs de départ pratiques et leurs Influence:

Paramètres Défauts typiques Valeurs de départ (exemple) Effet dans l'hébergement
net.core.rmem_max 212,992 B 16,777,216 B (16 MB) Augmente la Recevoir-tampon pour une bande passante élevée
net.core.wmem_max 212,992 B 16,777,216 B (16 MB) Étend le Envoyer-buffer pour les grands chunks
net.ipv4.tcp_rmem 4096 87380 16777216 4096 262144 16777216 Contrôle dynamique des fenêtres avec Mise à l'échelle
net.ipv4.tcp_wmem 4096 65536 16777216 4096 262144 16777216 Plus de mémoire tampon d'émission pour les burst-Trafic
net.core.somaxconn 128 4096-16384 Réduit les drops lors des assauts de connexion

Autotuning et fenêtres dynamiques

J'utilise l'autotuning intégré de la pile Linux (entre autres tcp_moderate_rcvbuf) au lieu de forcer globalement des tailles fixes. Le noyau met à l'échelle les buffers de réception de manière dynamique jusqu'à tcp_rmem[2] et les adapte à la perte, au RTT et à la mémoire disponible. Du côté de l'envoi, TCP Small Queues (TSQ) limite les files d'attente surdimensionnées afin de préserver le pacing et l'équité. Il est important pour moi de fixer des valeurs maximales suffisamment élevées, mais de choisir le niveau par défaut de manière à ce que les connexions ne démarrent pas avec des tampons trop importants. Je n'utilise les overrides per socket que de manière ciblée, lorsqu'une application a des profils clairement définis (p. ex. longues distances vidéo), afin que l'autotuning continue d'optimiser la grande masse.

Planification de la capacité : connexions et RAM

Plus de tampons par socket signifie plus de RAM-impression. C'est pourquoi je planifie de manière conservatrice : pour chaque connexion active, je compte avec une mémoire tampon Send+Receive et un overhead de métadonnées (SKB), qui s'élève souvent en réalité à 1,3-2× de la taille de la mémoire tampon pure. Avec 100k sockets simultanés et 1 Mo de mémoire tampon effective par socket, nous parlons rapidement de >100 Go, ce qui caractérise la topologie NUMA et les risques OOM. tcp_mem et net.core.optmem_max aident à fixer des limites supérieures globales. En parallèle, j'augmente ulimit -n, je surveille /proc/net/sockstat et je fais attention aux limites des ports éphémères et des descripteurs de fichiers. J'évite ainsi que les buffers optimisés ne deviennent des goulots d'étranglement de la mémoire lors des pics de charge.

Serveurs d'applications et réponses de grande taille

Je veille à ce que NGINX/Apache et PHP-FPM ne soient pas installés dans de minuscules fichiers. morceaux parce que cela déclenche inutilement le TCP. Les grands corps statiques bénéficient de sendfile et d'une compression GZIP judicieuse, tant que la charge CPU est prise en compte. Pour les API, un tampon d'envoi plus grand augmente les chances de faire passer rapidement des trames complètes dans le pipeline. Le TTFB diminue souvent parce que le noyau peut offrir plus de données par roundtrip et que l'application voit moins de temps d'attente. Je vérifie toujours tcp_nodelay et tcp_nopush dans le contexte de la charge de travail afin d'évaluer la latence et la qualité de service. Débit de manière cohérente.

Options par socket dans l'application

Dans les chemins de latence, j'utilise TCP_NODELAY lorsque les petites écritures sensibles au temps (par ex. les réponses RPC) ne doivent pas attendre d'autres données. Pour les transferts en masse, je préfère utiliser TCP_CORK (équivalent à tcp_nopush) sous Linux, afin que la pile regroupe les segments jusqu'à ce qu'un bloc significatif soit disponible. Avec TCP_NOTSENT_LOWAT, je contrôle la quantité de données non envoyées dans le noyau à partir de laquelle l'application ralentit l'écriture - ce qui est utile pour déclencher rapidement le backpressure. Je n'active QUICKACK que brièvement après les interactions, afin de forcer des suites d'attaques rapides. Les WebSockets et les flux gRPC gagnent à ce que j'introduise le write-batching dans l'application au lieu d'envoyer de très nombreuses mini-trames qui chauffent inutilement le chemin du buffer et de l'IRQ.

HTTP/2, HTTP/3 et modèles de diffusion en continu

Avec HTTP/2, plusieurs flux se trouvent sur une connexion TCP - c'est bien pour le head-of-line au niveau de l'app, mais en cas de pertes, HOL est conservé dans le TCP. Des tampons d'envoi plus grands et bien synchronisés aident à remplir efficacement le cwnd et à traiter les priorités sans dégrader la latence des petits flux. Je veille à ce que la priorisation du serveur ne laisse pas les petits flux interactifs mourir de faim. HTTP/3/QUIC fonctionne sur UDP et possède ses propres chemins de mise en mémoire tampon ; les principes de base tels que les fenêtres orientées BDP, le pacing et la récupération des pertes restent toutefois similaires. Dans les piles mixtes, je garde un œil sur les tampons TCP et UDP afin d'éviter qu'un protocole ne supplante l'autre en mémoire.

NUMA, THP et chemin de mémoire

Sur les machines à plusieurs socles, j'épingle les processus NUMA-numactl permet de placer les travailleurs et les accès mémoire sur le même nœud. Je désactive les Transparent Huge Pages lorsque je remarque une fragmentation ou des pics de latence. Une politique de mémoire cohérente empêche les threads réseau d'accéder à des banques éloignées et les caches de rester froids. L'application bénéficie ainsi d'un chemin de données fiable avec un temps de latence court. Durée de validité.

Stockage, cache de page et attente d'E/S

Je combine de grands tampons de filet avec NVMe-et beaucoup de RAM pour que le cache de pages fournisse des résultats. J'évite systématiquement le swapping, car chaque transfert augmente brusquement le temps de réponse. Je fais attention aux "dirty ratios" et aux intervalles de flux, sinon les écritures s'accumulent et bloquent les charges de lecture. Le monitoring via sar, perf et Prometheus montre si la charge I/O-Wait ou IRQ obstrue le chemin. La meilleure mémoire tampon réseau ne sert pas à grand-chose si le stockage freine sous la charge et que le CPU est en panne. Wait est suspendue.

Optimisation de la carte réseau et interruptions

Je règle la carte réseau sur Interruption-Le système de gestion de l'énergie de la machine est également un élément important. Receive-Side-Scaling répartit les flux sur les noyaux, tandis que RPS/RFS améliore l'allocation du CPU. J'utilise GRO/LRO et Checksum-Offload de manière ciblée, lorsqu'ils soulagent la pile sans générer de sensibilité à la latence. Ceux qui souhaitent approfondir les relations entre IRQ trouveront des indications pratiques sous Coalescence d'interruption. Avec l'épinglage des IRQ sur les bons noyaux, j'évite des coûts élevés. Cross-Sauts de la NUMA.

Files d'attente, AQM et pacing

Je préfère une discipline moderne de file d'attente egress avec pacing, comme fq ou fq_codel, afin que les flux soient traités de manière équitable et que les bursts soient lissés. Le BBR en particulier profite du fait que le noyau envoie en fonction du pacing et ne pousse pas de gros chunks de manière incontrôlée dans la NIC. Sur les chemins avec bufferbloat, j'utilise Active Queue Management pour maintenir la latence stable même en cas de charge. ECN peut aider à fournir des signaux de congestion précoces ; je vérifie toutefois que les middleboxes laissent passer ECN proprement. En outre, je surveille MTU et PMTU : Avec tcp_mtu_probing, je réagis aux blackholes, tandis que TSO/GSO/GRO déleste le chemin du CPU, sans pour autant brouiller la dynamique du roundtrip.

Backlog, somaxconn et flot de connexions

J'augmente somaxconn et les backlogs des serveurs d'apps pour que les vagues courtes ne provoquent pas d'erreurs de connexion, et Drops Les anneaux accept() et les workers pilotés par les événements maintiennent le chemin d'acceptation à flot. Les équilibreurs d'ingress devraient regrouper efficacement les contrôles de santé afin de ne pas devenir eux-mêmes des goulots d'étranglement. Côté TLS, je veille à ce que la session soit réutilisée et que les chiffrement soient modernes, afin que les handshake consomment moins de CPU. Ainsi, la file d'attente reste courte et l'application peut traiter rapidement chaque flux entrant. travailler.

Keepalives et cycle de vie des connexions

Je règle tcp_keepalive_time/-intvl/-probes de manière à ce que les connexions mortes soient rapidement détectées sans consommer inutilement de la bande passante. Dans les environnements très dynamiques, je raccourcis tcp_fin_timeout pour que les ressources soient libérées plus rapidement. Je protège le TIME-WAIT au lieu de l„“optimiser" : les reuse hacks apportent rarement de réels avantages, mais mettent en danger la correction. En cas de Long Polling et de HTTP/2-Idle-Streams, je place des timeouts côté application afin de ne pas parquer les buffers sur des sessions oubliées. Ainsi, les buffers restent disponibles pour les flux actifs et les serveurs restent réactifs.

Sécurité et résilience DoS

Je ne dois jamais considérer les grands tampons isolément, car ils augmentent la surface d'attaque de DoS élargir les possibilités. Le Rate-Limiting au niveau IP/chemin et les SYN-Cookies freinent les flux indésirables. Un WAF doit choisir la profondeur d'inspection en fonction du trafic afin de ne pas générer lui-même de latence. Les limites Conntrack, ulimit et les quotas per-IP protègent les ressources de l'épuisement. Ainsi, la boîte reste réactive, même si les Buffers sont plus grandes.

Conteneurs et virtualisation

Dans les conteneurs, je fais attention aux sysctls qui agissent dans l'espace de noms : de nombreux paramètres réseau s'appliquent à l'ensemble de l'hôte, d'autres nécessitent des sysctls de pod ciblés ou des privilèges. Dans Kubernetes, je définis les sysctls autorisés et les SecurityContexts, ou je règle les nœuds par DaemonSet. Les limites des Cgroups (Memory/CPU) ne doivent pas être transversales aux grands buffers de sockets, sinon il y a un risque d'OOM-kills lors des pics de charge. Dans les VM, je vérifie virtio-net vs. SR-IOV/Accelerated-Networking, l'attribution des IRQ et le coalescing sur l'hyperviseur. Le steal-time et la précision du timer influencent le pacing ; je choisis des sources d'horloge stables et mesure explicitement la gigue.

Observabilité opérationnelle

Au quotidien, je ne me fie pas uniquement aux comtes de throughput. Je regarde les tampons par socket avec ss -m/-ti, je lis les compteurs /proc/net/sockstat et netstat/nstat, et je corrige les retransmissions, les OutOfOrder, les RTO et les drops de liste. ethtool -S me montre les erreurs de NIC et les équilibres de queue, ip -s link les drops d'egress/ingress. Avec perf, eBPF/bpftrace et ftrace, je surveille tcp_retransmit_skb, les orbites skb et les hotspots SoftIRQ. Je lie les alertes aux SLO comme P50/P95 TTFB, aux pacing drops, au taux de retransmission et à la charge du backlog d'acceptation. Ainsi, je remarque rapidement si un changement de buffer apparemment mineur produit des effets latéraux.

Guide pratique : Pas à pas

Je commence par un état des lieux : RTT, débit, retransmissions et TTFB, ainsi que les profils CPU et IRQ. Ensuite, je règle rmem_max/wmem_max sur 16 Mo, j'augmente modérément tcp_rmem/tcp_wmem et je recharge sysctl. Ensuite, je fais des tests de charge et j'évalue si j'utilise plus de bande passante et si RTT reste stable. Si nécessaire, j'augmente la taille par incréments de 1 à 2 Mo et j'observe en même temps les chiffres de la mémoire et des sockets. Enfin, je fige les bonnes valeurs, je documente les changements et je planifie des tests réguliers. Critiques, Les modèles de trafic évoluent.

En bref

Des tampons de socket réglés de manière ciblée augmentent le Débit, Ils réduisent le RTT et déchargent le CPU. Je détermine la taille cible à partir de la bande passante et du RTT et je valide chaque étape par des tests de charge. Une pile TCP cohérente, des interruptions NIC optimisées et un chemin de stockage rapide complètent le résultat. Avec sysctl, je garde les paramètres du noyau maintenables et visibles grâce au logging. J'obtiens ainsi une livraison rapide et fiable dans l'hébergement, avec des temps de chargement et de téléchargement sensiblement plus courts pour les utilisateurs. constante Vivre la performance.

Derniers articles