HTTP 다중화는 여러 요청의 프레임을 한 연결에서 번갈아 운반하는 기능이지, 연결이 사용하는 전송·혼잡·CPU·버퍼 자원을 요청마다 복제하는 기능은 아닙니다. HTTP/2에서는 TCP의 하나의 순서 있는 바이트 흐름을, HTTP/3에서는 QUIC의 스트림별 전달과 여전히 공유되는 연결 자원을 각각 추적해야 짧은 요청의 지연 원인을 분리할 수 있습니다.
스트림 ID와 TCP 바이트 흐름
큰 다운로드 A와 짧은 RPC B가 HTTP/2 한 연결을 공유한다고 합시다. HTTP/2는 프레임에 스트림 ID를 붙여 응답을 섞어 보낼 수 있습니다. 하지만 TCP는 그 ID를 모르는 하나의 순서 있는 바이트 스트림입니다.
앞의 TCP 바이트가 유실되면 뒤에 도착한 B 프레임도 빠진 구간이 채워질 때까지 읽기 API에 전달되지 않을 수 있습니다. HTTP/1.1 파이프라이닝의 응답 순서 제약을 줄인 것과 TCP의 선두 지연을 제거한 것은 다릅니다.
서버 처리와 네트워크 전달 지연
예를 들어 B 핸들러가 t=10에 끝났어도 A의 앞 TCP 바이트가 t=40에 복구되면 B가 앱 read에서 보이는 시각은 t=40 이후가 됩니다. 반대로 B가 서버 큐에서 t=35까지 실행되지 않았다면 TCP 손실만으로 설명할 수 없습니다. 서버 완료, 패킷 손실·재전송, 클라이언트 read를 같은 요청 ID로 나누어 기록하는 것이 첫 진단입니다.
그림을 글로 읽기
- 앞 구간 누락: A 데이터 일부 포함
- 뒤의 B 프레임 도착: 수신 버퍼에서 대기 가능
- 앞 구간 재전송·복구
- 연속 바이트 전달: HTTP/2가 프레임 분리
- 앞 구간 누락 → 뒤의 B 프레임 도착: 순서에 빈 구간
- 뒤의 B 프레임 도착 → 앞 구간 재전송·복구: 복구 대기
- 앞 구간 재전송·복구 → 연속 바이트 전달: 순서 완성
B의 서버 핸들러는 이미 끝났을 수 있습니다. 서버 처리 지연과 클라이언트 전달 지연을 같은 원인으로 기록하지 않습니다. 연결 재전송·RTT·읽기 대기와 스트림별 실행 시간을 같은 요청 ID로 연결해야 합니다.
HTTP/2 DATA와 스트림·연결 흐름 제어 창
스트림 B의 창이 32KB 남아도 연결 전체 창이 0이면 B의 DATA 전송은 기다립니다. 반대로 연결 창이 64KB여도 B 창이 0이면 B는 기다리지만 다른 스트림은 자기 창이 있으면 진행할 수 있습니다. WINDOW_UPDATE와 수신자의 소비·버퍼 정책을 확인합니다.
| 스트림 창 | 연결 창 | DATA 전송 |
|---|---|---|
| 양수 | 양수 | 두 한도와 다른 조건 내 가능 |
| 0 | 양수 | 해당 스트림 대기 |
| 양수 | 0 | 연결의 DATA 전송 대기 |
이는 HTTP/2 계층의 흐름 제어입니다. 아래 TCP의 수신 창·혼잡 제어와는 별개입니다. 모든 제어 프레임까지 DATA와 같은 흐름 제어를 받는다고 일반화하지 않습니다. 앱이 메시지를 받아 무한 작업 큐에 쌓으면 HTTP 창만으로 앱 메모리는 보호되지 않습니다.
QUIC의 스트림별 전달 분리
HTTP/3의 QUIC은 각 스트림의 순서 상태를 따로 관리하므로 A의 누락 바이트 때문에 B의 이미 준비된 바이트 전달이 TCP처럼 직접 막히는 문제를 줄입니다. 그러나 한 패킷에 여러 스트림 데이터가 실릴 수 있고, 그 패킷을 잃으면 해당 데이터들이 함께 영향을 받습니다.
연결의 혼잡 제어·대역폭·암호화 CPU·서버 큐도 공유합니다. 혼잡 제어가 전송을 줄이면 이후 B의 새 데이터도 느려질 수 있습니다. HTTP 헤더 압축의 의존과 애플리케이션 작업 의존도 별도 대기를 만들 수 있어 “HTTP/3면 손실 영향 0”은 틀립니다.
큰 스트림과 작은 요청의 공정성
한 연결을 유지할지 다운로드 연결을 분리할지는 짧은 RPC p99와 연결·handshake·CPU 예산을 함께 비교해 결정합니다. HTTP/3 전환도 손실 시 스트림 전달 분리만 확인하지 말고 혼잡 창, 패킷 손실, 프록시 fallback, 서버 scheduler를 같은 경로에서 측정해야 합니다. 작은 요청이 계속 늦다면 스트림 우선순위보다 앱 버퍼나 서버 작업 큐가 원인일 수 있습니다.
큰 응답이 프록시·클라이언트 앱 버퍼를 차지하거나 서버 scheduler가 긴 작업을 먼저 처리하면 전송 프로토콜과 별개로 짧은 RPC가 지연됩니다. 우선순위 신호의 지원과 실제 구현이 그것을 어떻게 쓰는지 확인해야 합니다. 헤더를 지정했다고 끝나지 않습니다.
큰 다운로드를 별도 연결·풀로 분리하면 공유 지연을 줄일 수 있지만 연결마다 상태·handshake·혼잡 경쟁이 생깁니다. 전체 연결과 메모리 상한을 유지한 채 짧은 RPC p99·다운로드 처리량·CPU를 함께 비교합니다. 작은 요청만 빨라지고 전체 서비스가 불안정해지지 않게 해야 합니다.
동일 경로의 HTTP/2·HTTP/3 비교
손실·지연·재정렬, 느린 reader, 큰·작은 응답을 섞어 HTTP/2와 HTTP/3를 비교합니다. UDP 차단·MTU·프록시 지원·fallback도 실제 사용자 환경의 일부입니다. QUIC 선택은 신뢰성·혼잡을 검증된 계층에 맡기는 것이지 생 UDP로 앱이 모두 다시 구현한다는 뜻이 아닙니다.
네트워크 전달, 서버 실행, 연결·스트림 창, 앱 큐를 각각 측정합니다. 다중화는 많은 요청을 한 연결에 싣는 기능이며, 그 연결의 모든 자원을 독립시키는 기능은 아닙니다.