· kaynak dev.to (home feed)
Go 1.27, okunmamış HTTP/1 yanıt gövdelerini boşaltıyor ama 256 KB sonrasında bağlantılar yine de ölüyor
Go 1.27'nin net/http paketi artık Close sırasında okunmamış HTTP/1 yanıt gövdelerini boşaltıyor ve böylece bağlantılar boşta bekleyen havuza dönebiliyor; ama yalnızca yanıt 256 KB'dan küçükse ve boşaltma 50 ms içinde tamamlanırsa.

Go 1.27, standart kütüphanedeki HTTP istemcisinin, programın sonuna kadar okumadan kapattığı HTTP/1 yanıt gövdelerini ele alma biçimini değiştiriyor. Bağlantıyı çöpe atmak yerine, net/http.Transport artık kalan baytları kendisi boşaltıyor ve böylece bağlantı boşta bekleyen havuza dönebiliyor; ancak bu, sıkı sınırlar içinde: en fazla 256 KB ve en fazla 50 milisaniye.
Gabriel Anhaia'nın bir dev.to yazısında incelediği bu değişiklik, Go kod incelemelerindeki en yaygın notlardan birini hedefliyor: bağlantının yeniden kullanılabilmesi için resp.Body.Close() öncesine io.Copy(io.Discard, resp.Body) ekleyin. Go 1.27 bu alışkanlığı standart kütüphaneye taşıyor ve sınırlar da bağlantıların hâlâ nerede öldüğünü belirliyor.
Boşaltma neden önemli
HTTP/1, yanıtları tek bir TCP bağlantısı üzerinden art arda gönderir ve istemci, Content-Length veya Transfer-Encoding başlıklarının tanımladığı biçimde mevcut gövdeyi sonuna kadar tüketmeden bir sonraki isteğini gönderemez. Go 1.26 ve öncesinde gövdeyi erken kapatmak, transport'ın bağlantıyı bırakıp yenisini araması anlamına geliyordu; yeni bir TCP el sıkışması ve HTTPS için yeni bir TLS el sıkışmasıyla birlikte.
Go ekibinin 14 Ekim 2026'da Nicholas Husin ve Damien Neil tarafından yayımlanan ve Anhaia tarafından alıntılanan "Better HTTP/1 connection reuse" yazısı bir kıyaslama içeriyor: GODEBUG=http2client=0 ile HTTP/1'e zorlanan, index.golang.org'a yapılan 100 ardışık, yalnızca durum kodu okunan GET isteği, yazarın makinesinde Go 1.26'da yaklaşık 710 ms, Go 1.27'de ise yaklaşık 150 ms sürdü. Program değişmedi; yalnızca standart kütüphane farklı davrandı.
Eski başarısızlık kazayla tetiklenmesi kolaydı. Anhaia'nın da belirttiği gibi, bir json.Decoder, bir JSON gövdesinin sonundaki satır sonu karakterinden hemen önce okumayı bırakabilir ve tek bir okunmamış bayt bile bağlantıyı kaybetmek için yeterliydi.
Kesin kural
Go 1.27 sürüm notları yeni boşaltma işleminin muhafazakâr bir sınıra kadar çalıştığını描述 ediyor ve golang/go#77370 numaralı issue bu sınırı tanımlıyor: hangisi önce dolarsa 256 KB veya 50 milisaniye. 1.27 sürüm dalında, boşaltmayı deneme kararı transport.go içindeki tek bir satır: tryDrain := alive && resp.ContentLength <= maxPostCloseReadBytes. Anhaia bundan dört sonuç çıkarıyor.
Birincisi, kontrol tüm yanıtın bildirilen Content-Length değerini kullanıyor, okunmamış kalan bayt sayısını değil. 300 KB'lık bir gövde asla boşaltılmıyor; istemci son 100 bayt dışında tamamını okumuş olsa bile — ve otomatik boşaltma sözünün en yanıltıcı olduğu senaryo tam olarak bu.
İkincisi, chunked bir yanıtın ContentLength değeri -1 olduğu için kontrolden geçiyor. Boşaltma ardından en fazla 256 KB artı bir baytı bir goroutine içinde kopyalıyor ve bağlantıyı yalnızca EOF'a ulaşırsa tutuyor.
Üçüncüsü, boşaltma 50 ms sonra vazgeçiyor; dolayısıyla gövdenin ortasında durakslayan bir sunucu yine de bağlantıya mal oluyor.
Dördüncüsü, istek veya yanıtta Connection: close varsa hiç boşaltma yapılmıyor.
CL 741222 numarasıyla izlenen ilgili bir düzeltme sayesinde Close, boşaltma tamamlanmadan dönüyor; böylece boşaltma hiçbir zaman çağıranı bloke etmiyor. Ancak ardından gelen bir istek, hâlâ çalışan bir boşaltmayla zaman zaman yarışa girip yine de yeni bir bağlantı arayabiliyor.
HTTP/2 bunların hiçbirinden etkilenmiyor; çünkü çoklamalı akışları sayesinde bir akıştaki okunmamış gövde diğerlerini bloke etmiyor.
Kendiniz doğrulamak
Anhaia'nın gösterimi bağlantıları sunucu tarafında, yani ulaştıkları yerde sayıyor: http.Server.ConnState'e takılan bir hook her StateNew olayında bir sayacı artırıyor ve program, yapılandırılabilir gövde boyutu, okunan bayt sayısı, chunked kodlama ve gövde ortası duraklamalarıyla 100 GET isteği çalıştırıyor. Beklenen sonuçlar yukarıdaki kuraldan çıkıyor. Tamamı okunmamış 4 KB'lık bir gövde 1.27'de bağlantıları yeniden kullanmalıyken, yalnızca son 100 baytı okunmamış 300 KB'lık bir gövde, 400 KB'lık chunked bir gövde veya 100 ms'lik bir sunucu duraklaması her iki sürümde de yaklaşık istek başına bir bağlantı üretmeli. Anhaia bunları kendi bilgisayarında çalıştırmadığını belirtiyor; beklentiler koddan geliyor.
Mevcut kodla ne yapmak gerek
Yalnızca Go 1.27 veya sonrasıyla derlenen modüller için Anhaia'nın tavsiyesi, io.Copy(io.Discard, resp.Body) satırlarını silmek ve ertelenmiş Close'u korumak; bu, Go ekibinin gövdeyi sadece kapatın yönergesiyle de örtüşüyor. Şunu da ekliyor: sınırsız bir io.Copy zaten her zaman bir riskti; vazgeçmeyi düşündüğünüz çok gigabaytlık bir indirmeye yönlendirilirse, bütününü sizin goroutine'inizde okuyabilirdi. Sürüm notlarında yeni davranış için listelenmiş bir GODEBUG ayarı yok; yani 1.27 ile birlikte koşulsuz olarak geliyor.
Neden önemli
Bağlantı yeniden kullanımı bir HTTP istemcisindeki en büyük gizli performans kollarından biri ve Go ekibinin kendi kıyaslaması, yeniden kullanım çalıştığında sıradan bir istek örüntüsünde dört kattan fazla hızlanma gösteriyor. Yükseltme yapan ekipler için bu değişiklik, çoğu Go servisinin taşıdığı ortak kod kalıbını ortadan kaldırıyor. Ancak 256 KB ve 50 ms tavanları, kısmen okunan büyük yanıtların, 256 KB'yi aşan chunked yanıtların ve yavaş sunucuların bağlantıları hâlâ koparacağı anlamına geliyor. Otomatik davranışın tam olarak nerede durduğunu bilmek, bağlantı havuzunu sıcak tutan kod ile her istekte sessizce yeniden el sıkışan kodu ayıran şeydir.
- #golang
- #http
- #networking
- #performance
- #standard-library
İlgili yazılar
- Microsoft ve Lumen, Banka Verilerini İnternetten Uzak Tutan Özel Bağlantıyı Birlikte Satacak
- PostgreSQL 19, JIT derlemesini varsayılan olarak kapattı; analitik iş yükleri yeniden açmak zorunda
- Microsoft, Windows 11'de çoklu görevin 8 GB RAM'li bilgisayarlarda yüzde 40'a kadar daha hızlı olduğunu açıkladı