Outils de développement

Go 1.27 corrige la réutilisation des connexions HTTP/1 pour les corps de réponse non lus

Go 1.27 améliore les performances du client HTTP/1 en gérant automatiquement les corps de réponse non lus, permettant ainsi la réutilisation des connexions sans modifications manuelles du code.

Illustration of smooth data flow between servers representing improved HTTP connection reuse
Illustration générée pour cet article

Traduit automatiquement depuis l'original anglais.

L'équipe Go a publié la version 1.27, introduisant une amélioration significative des performances pour les clients HTTP/1 qui ne lisent pas entièrement les corps de réponse. Lors de benchmarks effectués contre index.golang.org, le nouveau runtime effectue 100 requêtes GET séquentielles en environ 150 millisecondes, contre 710 millisecondes dans Go 1.26. Cette accélération par cinq répond à une inefficacité de longue date où les données de réponse inutilisées empêchaient la réutilisation des connexions TCP.

Ce qui s'est passé

Les développeurs créant des services backend en Go ont longtemps rencontré des pénalités de performance silencieuses lors de l'utilisation du package standard net/http avec HTTP/1. Le problème survient lorsqu'un client effectue une requête mais ne consomme pas entièrement le corps de la réponse avant de le fermer. Dans Go 1.26 et les versions antérieures, le client HTTP détectait les données non lues et abandonnait la connexion TCP sous-jacente plutôt que de tenter de la réutiliser. Cela forçait la création d'une nouvelle connexion pour chaque requête suivante, entraînant le coût complet des poignées de main (handshakes) TCP et TLS à chaque fois.

L'amélioration apportée par Go 1.27 modifie ce comportement. Le runtime vide désormais automatiquement les corps de réponse non lus lorsque resp.Body.Close() est appelé, permettant à la connexion de rester active et disponible pour les futures requêtes. Ce changement cible spécifiquement le trafic HTTP/1, qui reste largement utilisé malgré la prévalence de HTTP/2. En éliminant le besoin pour les développeurs de vider manuellement les corps de réponse, la mise à jour réduit la latence et la consommation de ressources pour les services qui vérifient uniquement les codes de statut ou analysent des réponses JSON partielles.

Comment cela fonctionne

Pour comprendre la correction, il faut examiner comment HTTP/1 gère la transmission des données. Contrairement à HTTP/2, qui multiplexe plusieurs flux sur une seule connexion, HTTP/1 envoie les réponses séquentiellement comme un flux continu d'octets. Le client lit d'abord la ligne de statut et les en-têtes, puis le corps. Pour que la connexion soit réutilisée, le client doit lire chaque octet du corps de la réponse actuelle afin de savoir exactement où commence la réponse suivante. Si des octets restent non lus, le client ne peut pas déterminer en toute sécurité le début du message suivant, ce qui entraîne des erreurs de protocole si la connexion est réutilisée.

Dans les versions précédentes, si un développeur appelait resp.Body.Close() sans lire l'intégralité du corps, le package net/http marquait la connexion comme inutilisable. Il fermait ensuite la socket et en ouvrait une nouvelle pour la requête suivante. Ce processus implique une poignée de main TCP, qui prend un temps aller-retour (RTT), et une poignée de main TLS, qui peut prendre deux RTT pour TLS 1.2 ou un seul pour TLS 1.3. Sur les réseaux à haute latence, ces poignées de main dominent le temps total de la requête.

Go 1.27 modifie la méthode Close() pour lire et jeter automatiquement toutes les données restantes dans le tampon du corps de réponse avant de rendre la connexion au pool. Cela garantit que le flux est positionné correctement pour la prochaine requête. Le runtime gère cette opération efficacement, évitant tout blocage inutile tout en assurant la conformité au protocole. Ce mécanisme reproduit ce que de nombreux développeurs Go expérimentés implémentaient auparavant manuellement en copiant le corps vers io.Discard.

Détails clés

  • Go 1.27 réduit le temps nécessaire pour 100 requêtes GET HTTP/1 séquentielles de ~710 ms à ~150 ms lors des tests de benchmark.
  • L'amélioration s'applique spécifiquement aux connexions HTTP/1 ; HTTP/2 n'était pas affecté par ce problème grâce à son architecture de multiplexage.
  • Les solutions de contournement précédentes obligeaient les développeurs à vider manuellement les corps de réponse en utilisant io.Copy(io.Discard, resp.Body) avant la fermeture.
  • La correction consomme automatiquement les octets non lus lorsque resp.Body.Close() est invoqué, préservant la réutilisabilité de la connexion.
  • Les échecs de réutilisation de connexion se produisaient souvent dans le code qui vérifiait uniquement resp.StatusCode ou analysait du JSON sans lire les sauts de ligne finaux.
  • Le changement est compatible avec les versions antérieures et ne nécessite aucune modification du code pour que les applications existantes en bénéficient.

Pourquoi c'est important

Pour les ingénieurs logiciels construisant des microservices ou des clients API, cette mise à jour supprime une source courante de latence inattendue. De nombreuses applications ne se soucient que du succès ou de l'échec d'un appel API, indiqué par le code de statut HTTP, et ignorent le corps de la réponse. Sans cette correction, ces applications souffraient de mauvaises performances dues à une rotation constante des connexions. La surcharge liée à l'établissement de nouvelles connexions TCP et TLS est significative, surtout dans les systèmes distribués où les services communiquent entre régions avec une latence réseau plus élevée.

De plus, ce changement réduit la charge cognitive des développeurs. Auparavant, les équipes devaient imposer des normes de codage strictes pour garantir que tous les corps de réponse soient vidés, nécessitant souvent des fonctions wrapper personnalisées ou des règles de linter. Les instances manquées conduisaient à une dégradation subtile des performances, difficile à diagnostiquer car le code semblait correct et fonctionnel. Avec Go 1.27, la bibliothèque standard gère ce cas limite en interne, rendant l'utilisation sûre et efficace du client HTTP le comportement par défaut.

Ce que vous pouvez faire

  • Mettez à niveau votre chaîne d'outils Go vers la version 1.27 pour bénéficier des améliorations automatiques de réutilisation des connexions.
  • Examinez le code client HTTP existant pour identifier les endroits où les corps de réponse sont ignorés ou partiellement lus.
  • Supprimez la logique manuelle de vidage du corps telle que io.Copy(io.Discard, resp.Body) si elle avait été ajoutée uniquement pour la réutilisation des connexions.
  • Testez la latence de votre service sous charge pour observer la réduction de la surcharge liée à l'établissement des connexions.
  • Assurez-vous que vos outils de surveillance suivent les taux de réutilisation des connexions pour vérifier l'amélioration dans les environnements de production.
  • Continuez à utiliser HTTP/2 lorsque possible, mais notez que cette correction améliore significativement les performances résiduelles liées à HTTP/1.

Outils de la Boutique Bytechap

Continuer la lecture

Tous les articles