Entwicklerwerkzeuge

Go 1.27 behebt die Wiederverwendung von HTTP/1-Verbindungen bei nicht gelesenen Antwortkörpern

Go 1.27 verbessert die Leistung des HTTP/1-Clients, indem es nicht gelesene Antwortkörper automatisch verarbeitet und so die Verbindungswiederverwendung ohne manuelle Codeänderungen ermöglicht.

Illustration of smooth data flow between servers representing improved HTTP connection reuse
Für diesen Artikel generierte Illustration

Automatisch aus dem englischen Original übersetzt.

Das Go-Team hat Version 1.27 veröffentlicht und führt damit eine erhebliche Leistungsverbesserung für HTTP/1-Clients ein, die Antwortkörper nicht vollständig lesen. In Benchmarks gegen index.golang.org erledigt die neue Laufzeitumgebung 100 sequenzielle GET-Anfragen in etwa 150 Millisekunden, verglichen mit 710 Millisekunden in Go 1.26. Diese fünffache Beschleunigung behebt eine langjährige Ineffizienz, bei der ungenutzte Antwortdaten die Wiederverwendung von TCP-Verbindungen verhinderten.

Was ist passiert?

Entwickler, die Backend-Dienste in Go erstellen, stoßen seit Langem auf stille Leistungseinbußen bei der Verwendung des Standardpakets net/http mit HTTP/1. Das Problem tritt auf, wenn ein Client eine Anfrage stellt, den Antwortkörper aber vor dem Schließen nicht vollständig konsumiert. In Go 1.26 und früheren Versionen erkannte der HTTP-Client die ungelesenen Daten und verworf die zugrunde liegende TCP-Verbindung, anstatt zu versuchen, sie wiederzuverwenden. Dies erzwang die Erstellung einer neuen Verbindung für jede nachfolgende Anfrage, was jedes Mal die vollen Kosten für TCP- und TLS-Handshakes verursachte.

Die Verbesserung in Go 1.27 ändert dieses Verhalten. Die Laufzeitumgebung leert nun automatisch ungelesene Antwortkörper, wenn resp.Body.Close() aufgerufen wird, sodass die Verbindung aktiv bleibt und für zukünftige Anfragen verfügbar ist. Diese Änderung zielt speziell auf HTTP/1-Traffic ab, der trotz der Verbreitung von HTTP/2 noch weit verbreitet ist. Durch die Eliminierung der Notwendigkeit, dass Entwickler Körper manuell leeren müssen, reduziert das Update Latenz und Ressourcenverbrauch für Dienste, die nur Statuscodes prüfen oder teilweise JSON-Antworten parsen.

Wie funktioniert es?

Um die Korrektur zu verstehen, muss man betrachten, wie HTTP/1 die Datenübertragung handhabt. Im Gegensatz zu HTTP/2, das mehrere Streams über eine einzelne Verbindung multiplexed, sendet HTTP/1 Antworten sequenziell als kontinuierlichen Byte-Stream. Der Client liest zuerst die Statuszeile und die Header, dann den Body. Damit die Verbindung wiederverwendet werden kann, muss der Client jedes Byte des aktuellen Antwortkörpers lesen, um genau zu wissen, wo die nächste Antwort beginnt. Wenn Bytes ungelesen bleiben, kann der Client den Beginn der nächsten Nachricht nicht sicher bestimmen, was zu Protokollfehlern führt, wenn die Verbindung wiederverwendet wird.

In früheren Versionen markierte das Paket net/http die Verbindung als unbrauchbar, wenn ein Entwickler resp.Body.Close() aufrief, ohne den gesamten Body zu lesen. Es schloss dann den Socket und öffnete einen neuen für die nächste Anfrage. Dieser Prozess umfasst einen TCP-Handshake, der eine Round-Trip-Time (RTT) dauert, und einen TLS-Handshake, der zwei RTTs für TLS 1.2 oder eine für TLS 1.3 dauern kann. In Netzwerken mit hoher Latenz dominieren diese Handshakes die gesamte Anfragelzeit.

Go 1.27 modifiziert die Methode Close(), um automatisch alle verbleibenden Daten im Puffer des Antwortkörpers zu lesen und zu verwerfen, bevor die Verbindung zurück in den Pool gegeben wird. Dies stellt sicher, dass der Stream korrekt für die nächste Anfrage positioniert ist. Die Laufzeitumgebung behandelt dies effizient, vermeidet unnötiges Blockieren und gewährleistet gleichzeitig die Protokollkorrektheit. Dieser Mechanismus spiegelt wider, was viele erfahrene Go-Entwickler zuvor manuell implementierten, indem sie den Body in io.Discard kopierten.

Wichtige Details

  • Go 1.27 reduziert die Zeit für 100 sequenzielle HTTP/1-GET-Anfragen in Benchmark-Tests von ~710ms auf ~150ms.
  • Die Verbesserung gilt spezifisch für HTTP/1-Verbindungen; HTTP/2 war aufgrund seiner Multiplexing-Architektur von diesem Problem nicht betroffen.
  • Frühere Workarounds erforderten, dass Entwickler Antwortkörper manuell mit io.Copy(io.Discard, resp.Body) vor dem Schließen entleerten.
  • Die Korrektur konsumiert automatisch ungelesene Bytes, wenn resp.Body.Close() aufgerufen wird, und bewahrt so die Wiederverwendbarkeit der Verbindung.
  • Fehler bei der Verbindungswiederverwendung traten häufig in Code auf, der nur resp.StatusCode prüfte oder JSON ohne Lesen der abschließenden Zeilenumbrüche parste.
  • Die Änderung ist abwärtskompatibel und erfordert keine Codeänderungen, damit bestehende Anwendungen davon profitieren können.

Warum ist das wichtig?

Für Software-Ingenieure, die Microservices oder API-Clients entwickeln, entfernt dieses Update eine häufige Quelle unerwarteter Latenz. Viele Anwendungen interessieren sich nur für den Erfolg oder Misserfolg eines API-Aufrufs, angezeigt durch den HTTP-Statuscode, und ignorieren den Antwortkörper. Ohne diese Korrektur würden solche Anwendungen unter schlechter Leistung aufgrund ständiger Verbindungswechsel leiden. Der Overhead für die Einrichtung neuer TCP- und TLS-Verbindungen ist erheblich, besonders in verteilten Systemen, in denen Dienste über Regionen hinweg mit höherer Netzwerklatenz kommunizieren.

Darüber hinaus reduziert diese Änderung die kognitive Last für Entwickler. Früher mussten Teams strenge Codierungsstandards durchsetzen, um sicherzustellen, dass alle Antwortkörper entleert wurden, oft erforderlich durch benutzerdefinierte Wrapper-Funktionen oder Linter-Regeln. Verpasste Instanzen führten zu subtilen Leistungseinbußen, die schwer zu diagnostizieren waren, da der Code korrekt und funktional aussah. Mit Go 1.27 behandelt die Standardbibliothek diesen Sonderfall intern und macht die sichere und effiziente Nutzung des HTTP-Clients zum Standardverhalten.

Was Sie tun können

  • Aktualisieren Sie Ihre Go-Toolchain auf Version 1.27, um von den Verbesserungen bei der automatischen Verbindungswiederverwendung zu profitieren.
  • Überprüfen Sie bestehenden HTTP-Client-Code, um Stellen zu identifizieren, an denen Antwortkörper ignoriert oder teilweise gelesen werden.
  • Entfernen Sie manuelle Logik zum Entleeren des Körpers wie io.Copy(io.Discard, resp.Body), wenn diese ausschließlich zur Verbindungswiederverwendung hinzugefügt wurde.
  • Testen Sie die Latenz Ihres Dienstes unter Last, um die Reduzierung des Overheads für die Verbindungsherstellung zu beobachten.
  • Stellen Sie sicher, dass Ihre Monitoring-Tools die Raten der Verbindungswiederverwendung verfolgen, um die Verbesserung in Produktionsumgebungen zu überprüfen.

Tools aus dem Bytechap-Shop

Weiterlesen

Alle Artikel