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:
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:
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.
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:
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:
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:
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:
browser will always schedule exactly as I imagine
Lepszym narzędziem pozostaje kontrola dependency graph strony:
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:
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.