Konrad Kowalski (rootsher)Principal Platform & Reliability Architect110101111011111101011110011001010010001011111110

HTTP/2: multiplexing zmienił frontendowy network model

data
kategoria
Networking
także w
Frontend
czytanie
1 min / 288 słów

HTTP/2 zachowuje semantykę HTTP, ale zmienia wire format.

Request nie jest już tekstowym blokiem takim jak:

http
GET /app.js HTTP/1.1

HTTP/2 używa binary frames.

Streams

Każda wymiana request/response należy do własnego streamu.

Uproszczony model:

text
one TCP connection
│
├── stream 1: /app.js
├── stream 3: /styles.css
├── stream 5: /api/me
└── stream 7: /font.woff2

Frames różnych streamów mogą być przeplatane.

text
S1 HEADERS
S3 HEADERS
S1 DATA
S5 HEADERS
S3 DATA
S1 DATA

To jest multiplexing.

Wolna odpowiedź jednego requestu nie blokuje protokołu HTTP przed przesyłaniem frames innych streamów.

Mniej potrzeby na wiele connections

HTTP/1.1 uzyskiwał współbieżność przez kilka TCP connections.

HTTP/2 może obsługiwać wiele równoległych requestów przez jedno connection.

Dla frontendu zmniejsza to sens technik takich jak domain sharding.

Jedno dobrze wykorzystane connection może być bardziej efektywne niż kilka niezależnych TLS/TCP connections.

Header compression

Wiele requestów frontendowych ma podobne headers:

text
Origin
Cookie
Accept
User-Agent
...

HTTP/2 używa HPACK, aby kompresować HTTP fields pomiędzy wiadomościami na connection.

To szczególnie pomaga przy wielu małych requestach.

Nie oznacza jednak:

liczba requestów przestała mieć znaczenie.

Każdy request nadal ma:

  • server work,
  • scheduling,
  • bytes,
  • application latency,
  • cache consequences.

HTTP/2 usuwa część kosztu transportowego, nie koszt istnienia requestu.

TCP nadal jest wspólne

Tu pojawia się subtelność.

HTTP/2 multiplexuje wiele streams, ale wszystkie jadą po jednym TCP byte stream.

Jeżeli TCP zgubi segment:

text
packet loss
  |
  v
TCP must recover missing bytes
  |
  v
later bytes wait

Na poziomie transportu może to chwilowo zatrzymać dane należące do wielu HTTP/2 streams.

Czyli:

text
HTTP/2 stream independence
!=
transport independence

To jest transport-level head-of-line blocking.

Prioritization nie jest magicznym schedulerem frontendu

HTTP/2 ma mechanizmy priorytetyzacji, ale praktyczne zachowanie zależy od browsera, serwera i aktualnych mechanizmów priority signaling.

Frontend nie powinien projektować performance wokół założenia:

text
browser will always schedule exactly as I imagine

Lepszym narzędziem pozostaje kontrola dependency graph strony:

text
critical CSS
critical JS
lazy content
preload where justified

Co zobaczymy w DevTools?

W waterfall wiele requestów może rozpocząć się prawie jednocześnie.

Nie oznacza to wielu TCP connections.

Mogą być osobnymi HTTP/2 streams na jednym connection.

To ważne przy analizie performance:

text
parallel requests
!=
parallel sockets

HTTP/2 rozwiązał multiplexing na poziomie HTTP.

Ale nadal dziedziczył TCP.

HTTP/3 zachowuje podobny streamowy model HTTP, ale przenosi go na QUIC.