6호04 / 05

Go 1.27 — 읽지 않은 HTTP/1 응답 본문을 Transport가 알아서 비운다, 100회 요청이 710ms에서 150ms로

원문 Go Blog ↗

Go 블로그, 10월 14일, Nicholas Husin과 Damien Neil. Go 1.27의 net/http 변경을 설명합니다.

문제

HTTP/1은 한 연결에 응답이 순서대로 옵니다. 다음 응답을 읽으려면 이전 응답 본문을 끝까지 읽어야 합니다. Go 1.26까지 Transport는 본문을 다 읽지 않은 채 Body.Close()를 부르면 연결을 버리고 새 연결을 맺었습니다. TLS 핸드셰이크를 포함한 비용입니다.

걸리는 코드는 흔합니다.

  • 상태 코드만 보고 본문은 읽지 않는 코드
  • json.Decoder로 디코딩한 뒤 Close — 디코더가 끝의 개행 문자를 남겨 "다 읽지 않음"이 됨

그래서 많은 팀이 io.Copy(io.Discard, resp.Body)를 Close 전에 넣는 헬퍼를 두고 있었습니다. HTTP/2는 스트림 다중화라 이 문제가 없습니다.

변경

1.27의 Transport는 읽지 않은 HTTP/1 본문을 비동기로 드레인합니다. 한도는 50ms 또는 256KB — 그 안에 다 읽히면 연결을 재사용하고, 넘으면 버립니다. 응답에 Connection: close가 있으면 어차피 버릴 연결이므로 드레인하지 않습니다.

수치

GODEBUG=http2client=0으로 HTTP/1을 강제하고 index.golang.org에 100회 요청: Go 1.26 약 710ms, Go 1.27 약 150ms. 저자 머신, 저지연, TLS 1.3 조건입니다.

글에 없는 것

이 동작을 끄는 방법은 없습니다. 드레인 한도(50ms/256KB)를 조정하는 설정도 없습니다. 글은 이 동작이 향후 버전에서 바뀔 수 있다고만 밝힙니다.

실무에서 할 일

1.27로 올리면 수동 드레인 헬퍼를 지워도 됩니다. 글의 권고도 "직접 드레인하지 말고 다 쓰면 Close하라"입니다. HTTP/2에서는 애초에 불필요한 작업이었습니다.

주의할 점 하나. 큰 본문을 일부러 안 읽고 끊던 코드 — 예를 들어 헤더만 보고 큰 파일 다운로드를 중단하는 코드 — 는 이제 최대 50ms 또는 256KB를 더 읽습니다. 대부분 무시할 수준이지만, 그런 패턴이 핫패스에 있으면 한 번 재보세요.

  1. 01Telegram Desktop 원클릭 계정 탈취 — 링크 하나로 세션 파일을 빼간다, 조용히 고쳐졌다
  2. 02광고 타겟팅이 스파이웨어 배달 경로가 됐다 — Citizen Lab의 AdFection 보고서
  3. 03"패스키는 함정이다" vs "아니다" — 논쟁의 실제 쟁점은 어테스테이션 하나
  4. 04Go 1.27 — 읽지 않은 HTTP/1 응답 본문을 Transport가 알아서 비운다, 100회 요청이 710ms에서 150ms로지금 읽는 글
  5. 05윤초가 끝난다 — 2027년 5월 20일부터 연속 UTC, UT1과의 허용 차이는 1시간

2026년 10월 15일에 올라온 글

그날 대시보드 →
  1. ·광고 타겟팅이 스파이웨어 배달 경로가 됐다 — Citizen Lab의 AdFection 보고서

댓글

불러오는 중…