Wprowadzenie do HTTP/2

HTTP/2 sprawi, że nasze aplikacje będą szybsze, prostsze i bardziej niezawodne (rzadkie połączenie). Dzięki temu będziemy mogli cofnąć wiele z obejścia HTTP/1.1, które były wcześniej dostępne w naszych aplikacjach, i rozwiązać te problemy w samej warstwie transportu. Co więcej, otwiera też zupełnie nowe możliwości optymalizacji aplikacji i zwiększania wydajności.

Głównymi celami w przypadku HTTP/2 są zmniejszenie czasu oczekiwania przez włączenie pełnego multipleksowania żądań i odpowiedzi, zminimalizowanie obciążenia protokołu przez efektywną kompresję pól nagłówka HTTP oraz dodanie obsługi priorytetów żądań i wypychania z serwera. Aby wdrożyć te wymagania, trzeba skorzystać z licznych ulepszeń innych protokołów, takich jak sterowanie przepływem, obsługa błędów i mechanizmy uaktualniania. Są to jednak najważniejsze funkcje, które każdy programista stron internetowych powinien zrozumieć i wykorzystać w swoich aplikacjach.

HTTP/2 w żaden sposób nie zmienia semantyki aplikacji HTTP. Nadal będą obowiązywać wszystkie podstawowe pojęcia, takie jak metody HTTP, kody stanu, identyfikatory URI i pola nagłówka. Zamiast tego HTTP/2 modyfikuje format danych (ramkę) i przenoszenie danych między klientem a serwerem. Oba te procesy umożliwiają zarządzanie całym procesem i ukrywają jego złożoność przed naszymi aplikacjami w nowej warstwie ramek. W rezultacie wszystkie istniejące aplikacje można pobrać bez wprowadzania zmian.

Dlaczego nie HTTP/1.2?

Aby osiągnąć cele związane z wydajnością ustalone przez grupę roboczą HTTP, w ramach protokołu HTTP/2 wprowadzamy nową warstwę binarnej kadrowania, która nie jest zgodna wstecznie z poprzednimi serwerami i klientami HTTP/1.x. Z tego powodu główna wersja protokołu zwiększa się do HTTP/2.

Jeśli więc nie wdrażasz serwera WWW (lub klienta niestandardowego) przy użyciu nieprzetworzonych gniazd TCP, nie zauważysz żadnej różnicy: wszystkie nowe ramki niskiego poziomu są wykonywane przez klienta i serwer w Twoim imieniu. Jedynymi obserwowanymi różnicami będą poprawienie wydajności i dostępności nowych funkcji, takich jak ustalanie priorytetów żądań, sterowanie przepływem i przekazywanie danych z serwera.

Krótka historia SPDY i HTTP/2

SPDY to protokół eksperymentalny, stworzony w Google i ogłoszony w połowie 2009 roku. Jego głównym celem było zmniejszenie czasu oczekiwania na wczytanie stron internetowych przez eliminację dobrze znanych ograniczeń wydajności związanych z protokołem HTTP/1.1. Cele projektu zostały ustalone w ten sposób:

  • Zwiększenie czasu wczytywania strony o 50% (PLT).
  • Unikniesz wprowadzania zmian w treściach przez autorów witryn.
  • Ogranicz złożoność wdrażania i unikaj zmian w infrastrukturze sieciowej.
  • Opracuj nowy protokół we współpracy ze społecznością użytkowników open source.
  • Zbierz rzeczywiste dane o skuteczności, aby (na) zweryfikować protokół eksperymentalny.

Niedługo po tym ogłoszeniu, Mike Belshe i Roberto Peon, inżynierowie oprogramowania w Google, zaprezentowali swoje pierwsze wyniki, dokumentację i kod źródłowy eksperymentalnej implementacji nowego protokołu SPDY:

Jak dotąd testowaliśmy SPDY tylko w warunkach laboratoryjnych. Wstępne wyniki są bardzo zachęcające: po pobraniu 25 najpopularniejszych witryn przy użyciu symulowanego połączenia sieciowego odnotowujemy znaczną poprawę wydajności – strony wczytują się nawet o 55% szybciej. (Blog Chromium)

W 2012 r. wprowadziliśmy obsługę nowego protokołu eksperymentalnego w Chrome, Firefoksie i Operze, a SPDY w infrastrukturze zaczęły być wdrażane w coraz większej liczbie witryn, zarówno dużych (np. Google, Twitter, Facebook), jak i małych. W efekcie platforma SPDY mogła stać się standardem przez coraz większą popularność w branży.

Obserwując ten trend, spółka HTTP-WG (HTTP-WG) zapoczątkowała nowy cel, wykorzystując wnioski płynące z SPDY, opracowywać i ulepszać te funkcje oraz stworzyć oficjalny standard „HTTP/2”. Opracowano nowy statut, ogłoszono otwarte zaproszenie do przyjmowania propozycji HTTP/2. Po obszernej dyskusji w grupie roboczej przyjęto specyfikację SPDY jako punkt wyjścia dla nowego protokołu HTTP/2.

W ciągu kilku kolejnych lat SPDY i HTTP/2 współpracowały równolegle, a SPDY działało jako eksperymentalna gałąź, która służyła do testowania nowych funkcji i propozycji standardu HTTP/2. To, co wygląda dobrze na papierze, może nie sprawdzić się w praktyce i odwrotnie. SPDY zaoferowało możliwość przetestowania i oceny każdej propozycji przed jej włączeniem do standardu HTTP/2. Ostatecznie ten proces trwał 3 lata i opracowaliśmy kilkanaście wersji roboczych średnio zaawansowanych:

  • Marzec 2012 r.: zapytania ofertowe dotyczące HTTP/2
  • Listopad 2012 roku: pierwsza wersja robocza HTTP/2 (na podstawie SPDY)
  • Sierpień 2014 roku: opublikowanie wersji roboczej HTTP/2 i HPACK w wersji roboczej-12
  • Sierpień 2014 roku: ostatnie wywołanie grupy roboczej HTTP/2
  • Luty 2015 roku: zatwierdzone przez IESG wersje robocze HTTP/2 i HPACK
  • Maj 2015 roku: opublikowano standard RFC 7540 (HTTP/2) i RFC 7541 (HPACK)

Na początku 2015 roku organizacja IESG sprawdził i zatwierdził nowy standard HTTP/2 publikacji. Niedługo potem zespół Google Chrome ogłosił plan wycofania rozszerzeń SPDY i NPN dla protokołu TLS:

Główne zmiany protokołu HTTP/2 w stosunku do HTTP/1.1 miały na celu zwiększenie wydajności. Niektóre kluczowe funkcje, takie jak multipleksowanie, kompresja nagłówków, ustalanie priorytetów i negocjowanie protokołu, wywodziły się z prac przeprowadzonych we wcześniejszej otwartej, ale niestandardowej wersji protokołu o nazwie SPDY. Protokół SPDY działa w Chrome od wersji Chrome 6, ale większość jego zalet znajduje się w protokole HTTP/2, więc pora się pożegnać. Planujemy usunąć obsługę SPDY na początku 2016 roku. Jednocześnie usuniemy też obsługę rozszerzenia TLS o nazwie NPN na rzecz ALPN w Chrome. Zdecydowanie zalecamy programistom serwerów przejście na HTTP/2 i ALPN.

Z przyjemnością uczestniczymy w procesie otwartych standardów, który doprowadził do HTTP/2. Mamy nadzieję, że uda nam się go powszechnie wdrożyć, biorąc pod uwagę szerokie zaangażowanie branży w standaryzację i wdrażanie. (Blog Chromium)

Koewolucja technologii SPDY i HTTP/2 dla programistów serwerów, przeglądarek i stron internetowych, dzięki której można w praktyce poznać tworzony protokół. W rezultacie standard HTTP/2 należy do najlepszych i najdokładniej przetestowanych standardów. Zanim protokół HTTP/2 został zatwierdzony przez IESG, było już dziesiątki dokładnie przetestowanych, gotowych do wdrożenia w środowiskach produkcyjnych dla klientów i serwerów. Już kilka tygodni po zatwierdzeniu ostatecznej wersji protokołu wielu użytkowników zaczęło korzystać z jego zalet, ponieważ kilka popularnych przeglądarek (i wiele witryn) wdrożyło pełną obsługę HTTP/2.

Projekty i cele techniczne

Wcześniejsze wersje protokołu HTTP zostały celowo zaprojektowane z myślą o łatwości wdrożenia: HTTP/0.9 był protokołem jednowierszowym do wczytywania sieci WWW; HTTP/1.0 udokumentował popularne rozszerzenia do HTTP/0.9 w standardzie informacyjnym; protokół HTTP/1.1 wprowadził oficjalny standard IETF; zobacz Brief History of HTTP. Protokół HTTP/0.9-1.x działał dokładnie tak, jak powinien: HTTP jest jednym z najpopularniejszych protokołów aplikacji w internecie.

Niestety, prostota implementacji wpływa też na wydajność aplikacji: klienty HTTP/1.x muszą używać wielu połączeń, aby osiągnąć równoczesność i zmniejszyć czas oczekiwania; HTTP/1.x nie kompresuje nagłówków żądań i odpowiedzi, co powoduje niepotrzebnego ruchu w sieci; HTTP/1.x nie umożliwia efektywnego określania priorytetów zasobów, co skutkuje słabym wykorzystaniem połączenia TCP.

Ograniczenia te nie były śmiertelne, ale wraz ze wzrostem zakresu, złożoności i znaczenia aplikacji internetowych w codziennym życiu coraz bardziej obciążały zarówno programistów, jak i użytkowników internetu. Tę lukę, jaką zaprojektowano w celu rozwiązania problemu HTTP/2:

HTTP/2 umożliwia bardziej efektywne wykorzystanie zasobów sieciowych i krótszy czas oczekiwania dzięki kompresji pól nagłówka oraz umożliwieniu wielu równoczesnych wymian w tym samym połączeniu... W szczególności umożliwia przeplatanie komunikatów żądań i odpowiedzi w tym samym połączeniu i wykorzystuje wydajne kodowanie pól nagłówka HTTP. Umożliwia też ustalanie priorytetów żądań, co umożliwia szybsze wykonywanie ważniejszych żądań, co jeszcze bardziej zwiększa wydajność.

Powstały protokół jest bardziej przyjazny dla sieci, ponieważ można użyć mniejszej liczby połączeń TCP niż HTTP/1.x. Oznacza to mniejszą konkurencję z innymi przepływami i dłuższe połączenia, co z kolei prowadzi do lepszego wykorzystania dostępnej przepustowości sieci. HTTP/2 umożliwia też bardziej efektywne przetwarzanie wiadomości dzięki zastosowaniu binarnego kadrowania wiadomości. (Hypertext Transfer Protocol w wersji 2, wersja robocza 17)

Pamiętaj, że HTTP/2 rozszerza, a nie zastępuje, poprzednie standardy HTTP. Semantyka aplikacji HTTP jest taka sama i nie wprowadziliśmy żadnych zmian w oferowanych funkcjach ani podstawowych koncepcjach, takich jak metody HTTP, kody stanu, identyfikatory URI czy pola nagłówka. Te zmiany wyraźnie wykraczają poza zakres projektów HTTP/2. Chociaż ogólny interfejs API pozostaje taki sam, ważne jest, aby zrozumieć, jak zmiany na niskim poziomie radzą sobie z ograniczeniami wydajności u poprzednich protokołów. Omówmy pokrótce warstwę ramek binarnego i jej funkcje.

Warstwa ramek binarnych

Podstawą wszystkich ulepszeń wydajności HTTP/2 jest nowa warstwa ramek binarnych, która określa sposób szyfrowania i przesyłania wiadomości HTTP między klientem a serwerem.

Warstwa ramek binarnych HTTP/2

„Warstwa” oznacza wybór projektu polegający na wprowadzeniu nowego zoptymalizowanego mechanizmu kodowania między interfejsem gniazda a wyższym interfejsem API HTTP dostępnym dla naszych aplikacji: nie wpływa to na semantykę HTTP, taką jak czasowniki, metody i nagłówki, ale inaczej sposób kodowania, podczas przesyłania. W przeciwieństwie do protokołu HTTP/1.x rozdzielanego znakami nowego wiersza cała komunikacja HTTP/2 jest dzielona na mniejsze wiadomości i ramki, z których każda jest zakodowana w formacie binarnym.

W związku z tym zarówno klient, jak i serwer muszą używać nowego mechanizmu kodowania binarnego, aby się wzajemnie rozumieć: klient HTTP/1.x nie rozpozna serwera tylko dla HTTP/2 i odwrotnie. Na szczęście nasze aplikacje są spokojnie nieświadome tych zmian, ponieważ klient i serwer wykonują za nas wszystkie niezbędne elementy kadrowania.

Strumienie, wiadomości i ramki

Wprowadzenie nowego mechanizmu binarnego kadrowania zmienia sposób wymiany danych między klientem a serwerem. Aby opisać ten proces, zapoznajmy się z terminologią HTTP/2:

  • Strumień: dwukierunkowy przepływ bajtów w ustalonym połączeniu, który może zawierać jedną lub więcej wiadomości.
  • Wiadomość: pełna sekwencja klatek mapowanych na logiczne żądanie lub odpowiedź.
  • Frame: najmniejsza jednostka komunikacji w HTTP/2. Każda z nich zawiera nagłówek ramki, który przynajmniej identyfikuje strumień, do którego należy ramka.

Relacja tych terminów można streścić w następujący sposób:

  • Cała komunikacja jest wykonywana za pomocą pojedynczego połączenia TCP, które może przenosić dowolną liczbę strumieni dwukierunkowych.
  • Każdy strumień ma unikalny identyfikator i opcjonalne informacje o priorytecie, które są używane do przesyłania wiadomości dwukierunkowych.
  • Każda wiadomość jest logiczną wiadomością HTTP, np. żądaniem lub odpowiedzią, która składa się z co najmniej jednej ramki.
  • Ramka to najmniejsza jednostka komunikacji, w której znajdują się dane określonego typu, np. Nagłówki HTTP, ładunek wiadomości itd. Ramki z różnych strumieni mogą być przeplatane, a potem łączone za pomocą identyfikatora strumienia umieszczonego w nagłówku każdej ramki.

strumienie, wiadomości i ramki HTTP/2;

W skrócie HTTP/2 dzieli komunikację protokołu HTTP na wymianę ramek zakodowanych w postaci binarnych, które są następnie mapowane na wiadomości należące do konkretnego strumienia, z których wszystkie są multipleksowane w ramach pojedynczego połączenia TCP. Stanowi podstawę, która umożliwia wszystkie inne funkcje i optymalizacje wydajności udostępniane przez protokół HTTP/2.

Multipleksowanie żądań i odpowiedzi

W przypadku HTTP/1.x, jeśli klient chce wysyłać wiele równoległych żądań w celu zwiększenia wydajności, trzeba użyć wielu połączeń TCP (zobacz Korzystanie z wielu połączeń TCP). To działanie jest bezpośrednią konsekwencją modelu dostarczania HTTP/1.x, który zapewnia, że naraz można dostarczyć tylko jedną odpowiedź na połączenie (kolejka odpowiedzi). Co gorsza, powoduje to też blokowanie nadmiarowych informacji i nieefektywne korzystanie z bazowego połączenia TCP.

Nowa warstwa ramek binarnych w HTTP/2 eliminuje te ograniczenia i umożliwia multipleksowanie żądań i odpowiedzi, umożliwiając klientowi i serwerowi podział wiadomości HTTP na niezależne ramki, przeplatanie ich, a następnie ponowne łączenie ich po drugiej stronie.

Multipleksowanie żądań i odpowiedzi HTTP/2 w ramach połączenia współdzielonego

Zrzut dysku obejmuje kilka transmisji podczas tego samego połączenia. Klient przesyła do serwera ramkę DATA (strumień 5), natomiast serwer przesyła do klienta przeplataną sekwencję klatek dla strumieni 1 i 3. W rezultacie w transmisji są 3 równoległe strumienie.

Najważniejszym ulepszeniem HTTP/2 jest możliwość podzielenia wiadomości HTTP na niezależne ramki, przeplatania ich, a potem ponownego złożenia ich z drugiej strony. Efektem jest szereg korzyści płynących z wydajności, które obejmują cały stos wszystkich technologii internetowych. Dzięki temu możemy:

  • Przeplatać równolegle wiele żądań bez blokowania żadnego z nich.
  • Przeplatać równolegle wiele odpowiedzi bez blokowania żadnej z nich.
  • Używaj jednego połączenia, aby równolegle dostarczać wiele żądań i odpowiedzi.
  • Usuń zbędne obejścia HTTP/1.x (zobacz Optymalizacja pod kątem HTTP/1.x, takie jak połączone pliki, sprite’y obrazu i fragmentacja domeny).
  • Skróć czas wczytywania strony, eliminując niepotrzebne opóźnienia i poprawiając wykorzystanie dostępnej przepustowości sieci.
  • I wiele więcej...

Nowa warstwa kadrowania binarnego w HTTP/2 rozwiązuje problem blokujący czołówkę, napotkany w protokole HTTP/1.x, oraz eliminuje potrzebę używania wielu połączeń w celu umożliwienia równoległego przetwarzania oraz dostarczania żądań i odpowiedzi. Dzięki temu wdrażanie naszych aplikacji jest szybsze, prostsze i tańsze.

Określanie priorytetów transmisji

Gdy wiadomość HTTP można podzielić na wiele pojedynczych ramek i zezwalamy na multipleksowanie ramek z wielu strumieni, kolejność, w jakiej klatki są przeplatane i dostarczana zarówno przez klienta, jak i serwer, staje się ważną kwestią dotyczącą wydajności. Aby to ułatwić, standard HTTP/2 pozwala każdemu strumieniowi przypisać przypisaną wagę i zależność:

  • Każdemu strumieniowi można przypisać wagę całkowitą z zakresu od 1 do 256.
  • Do każdego strumienia może zostać przypisana wyraźna zależność od innego strumienia.

Połączenie zależności i wag strumienia pozwala klientowi zbudować i przekazać „drzewo określania priorytetów”, które określa, jak preferuje otrzymywanie odpowiedzi. Z kolei serwer może wykorzystać te informacje, aby nadać priorytet przetwarzaniu strumienia, kontrolując przydział procesora, pamięci i innych zasobów. Gdy dane odpowiedzi będą dostępne, przydzielamy przepustowość, aby zapewnić optymalne dostarczanie odpowiedzi o wysokim priorytecie do klienta.

Zależności i wagi strumienia HTTP/2

Zależność strumienia w HTTP/2 jest deklarowana przez podanie unikalnego identyfikatora innego strumienia jako jego nadrzędnego. Jeśli identyfikator zostanie pominięty, strumień jest zależny od „strumienia głównego”. Zadeklarowanie zależności strumienia wskazuje, że w miarę możliwości do strumienia nadrzędnego należy przydzielać zasoby przed zależnościami. Inaczej mówiąc: „przetwórz i dostarcz odpowiedź D przed odpowiedzią C”.

Strumienie, które współdzielą ten sam element nadrzędny (czyli strumienie równorzędne), powinny przydzielać zasoby proporcjonalnie do ich wagi. Jeśli np. strumień A ma wagę 12, a równorzędny B ma wagę 4, to aby określić odsetek zasobów, które powinien otrzymać każdy z nich:

  1. Sumuj wszystkie wagi: 4 + 12 = 16
  2. Podziel każdy strumień przez wagę całkowitą: A = 12/16, B = 4/16

Strumień A powinien więc otrzymywać 3 czwarte zasobów, a strumień B – 1/4 dostępnych zasobów; strumień B powinien otrzymywać jedną trzecią zasobów przydzielonych do strumienia A. Przyjrzyjmy się kilku praktycznym przykładom na ilustracji powyżej. Od lewej do prawej:

  1. Ani strumień A, ani B nie określa zależności nadrzędnej i uważa się, że jest zależny od niejawnego „strumienia głównego”. Strumień A ma wagę 12, a B – 4. Zatem, na podstawie proporcjonalnych wag, strumień B powinien otrzymywać jedną trzecią zasobów przydzielonych do strumienia A.
  2. Strumień D jest zależny od strumienia głównego, a C – od strumienia D. Dlatego użytkownik D powinien otrzymać pełny przydział zasobów przed grupą C. Wagi są nieistotne, ponieważ zależność C przekazuje silniejszą preferencję.
  3. Strumień D powinien otrzymać pełny przydział zasobów przed strumieniem C; strumień C powinien otrzymać pełny przydział zasobów przed strumieniami A i B; strumień B powinien otrzymywać jedną trzecią zasobów przydzielonych do strumienia A.
  4. Strumień D powinien otrzymać pełny przydział zasobów przed treściami E i C; kanały E i C powinny otrzymać równy podział przed A i B; A i B powinny otrzymywać przydzielanie proporcjonalne na podstawie swoich wag.

Jak pokazano w powyższych przykładach, połączenie zależności i wag strumienia zapewnia wyrazisty język określania priorytetów zasobów, co jest kluczowym elementem zwiększania wydajności przeglądania, gdy mamy wiele typów zasobów o różnych zależnościach i wagach. Co więcej, protokół HTTP/2 umożliwia też klientowi aktualizowanie tych preferencji w dowolnym momencie, co umożliwia dalsze optymalizowanie działania przeglądarki. Inaczej mówiąc, możemy zmieniać zależności i przenosić wagi w odpowiedzi na interakcje użytkownika i inne sygnały.

1 połączenie na punkt początkowy

Dzięki nowemu mechanizmowi ramek plików binarnych HTTP/2 nie potrzebuje już wielu połączeń TCP z równoległymi strumieniami Multiplex. Każdy strumień jest podzielony na wiele ramek, które można przeplatać i nadawać im priorytety. W rezultacie wszystkie połączenia HTTP/2 są trwałe i wymagane jest tylko jedno połączenie z każdym punktem początkowym, co przynosi liczne korzyści w zakresie wydajności.

Zarówno w przypadku SPDY, jak i HTTP/2 cechą decydującą jest dowolne multipleksowanie w kanale kontrolowanym przez zatłoczenie. Niesamowite, że to ważne i jak dobrze to działa. Bardzo cenię sobie ułamek tworzonych połączeń, które zawierają tylko jedną transakcję HTTP (i w ten sposób generują cały nakład pracy). W przypadku HTTP/1 74% naszych aktywnych połączeń zawiera tylko jedną transakcję, a trwałe połączenia nie są tak pomocne, jakbyśmy wszyscy chcieli. Jednak w HTTP/2 ta liczba spada do 25%. To ogromna korzyść w zakresie redukcji kosztów ogólnych. (Protokół HTTP/2 jest dostępny w przeglądarce Firefox, Patrick McManus)

Większość transferów HTTP trwa krótko i szybko, natomiast port TCP jest zoptymalizowany pod kątem długotrwałego zbiorczego przesyłania danych. Ponowne wykorzystanie tego samego połączenia pozwala HTTP/2 efektywniej korzystać z każdego połączenia TCP oraz znacznie zmniejszyć ogólny narzut protokołu. Ponadto użycie mniejszej liczby połączeń zmniejsza ilość pamięci i ilości przetwarzania na pełnej ścieżce połączenia (czyli klienta, pośrednika i serwera źródłowego). Pozwala to zmniejszyć ogólne koszty operacyjne oraz poprawić wykorzystanie i wydajność sieci. W rezultacie przejście na HTTP/2 nie tylko zmniejszy opóźnienia w sieci, lecz także pomoże zwiększyć przepustowość i obniżyć koszty operacyjne.

Sterowanie przepływem

Kontrola przepływu to mechanizm zapobiegający przeciążeniu odbiorcy danymi, których może nie chcieć lub nie może przetworzyć. Odbiorca może być zajęty, przeciążony lub może chcieć przydzielić stałą ilość zasobów na określony strumień. Na przykład klient mógł zażądać dużego strumienia wideo o wysokim priorytecie, ale użytkownik wstrzymał wideo, a teraz klient chce wstrzymać lub ograniczyć przesyłanie z serwera, aby uniknąć pobierania i buforowania niepotrzebnych danych. Serwer proxy może też mieć szybkie połączenia z kierunku przesyłania i wolne w pobliżu oraz chce regulować szybkość dostarczania danych tak, by odpowiadała szybkości strumienia z powrotem, aby kontrolować wykorzystanie zasobów itd.

Czy powyższe wymagania przypominają o sterowaniu przepływem TCP? Powinny, ponieważ problem jest w praktyce identyczny (patrz Kontrola przepływu). Strumienie HTTP/2 są jednak multipleksowane w ramach jednego połączenia TCP, więc kontrola przepływu TCP nie jest wystarczająco szczegółowa i nie zapewnia interfejsów API na poziomie aplikacji niezbędnych do regulacji przesyłania poszczególnych strumieni. Aby rozwiązać ten problem, HTTP/2 udostępnia zestaw prostych elementów składowych, które pozwalają klientowi i serwerowi wdrożyć własną kontrolę przepływu na poziomie strumienia i połączenia:

  • Sterowanie przepływem jest kierunkowe. Każdy odbiornik może ustawić dowolny rozmiar okna dla każdego strumienia i całego połączenia.
  • Sterowanie przepływem jest oparte na kredytach. Każdy odbiornik rozgłasza swoje początkowe połączenie i okno kontroli przepływu strumienia (w bajtach), które jest zmniejszane za każdym razem, gdy nadawca emituje ramkę DATA, a jej liczba zwiększa się przez ramkę WINDOW_UPDATE wysyłaną przez odbiornik.
  • Sterowania przepływem nie można wyłączyć. Po ustanowieniu połączenia HTTP/2 ramki SETTINGS wymieniają klienty i serwery, które ustawiają rozmiary okna kontroli przepływu w obu kierunkach. Domyślna wartość okna sterowania przepływem jest ustawiona na 65 535 bajtów, ale odbiorca może ustawić duży maksymalny rozmiar okna (2^31-1 B) i utrzymywać go, wysyłając ramkę WINDOW_UPDATE za każdym razem, gdy odbierane są jakiekolwiek dane.
  • Sterowanie przepływem odbywa się krok po kroku, a nie kompleksowe. Oznacza to, że pośrednik może za jego pomocą kontrolować wykorzystanie zasobów i wdrażać mechanizmy ich przydzielania na podstawie własnych kryteriów i metody heurystyki.

HTTP/2 nie określa żadnego konkretnego algorytmu implementacji kontroli przepływu. Zamiast tego udostępnia proste elementy składowe i opóźnia implementację klientowi i serwerowi, dzięki czemu mogą wdrożyć niestandardowe strategie regulacji wykorzystania i przydzielania zasobów, a także wdrażać nowe możliwości dostarczania, które mogą poprawić zarówno rzeczywistą, jak i widoczną wydajność naszych aplikacji internetowych (zobacz Szybkość, wydajność i percepcja człowieka).

Na przykład sterowanie przepływem w warstwie aplikacji umożliwia przeglądarce pobieranie tylko części konkretnego zasobu, wstrzymanie pobierania przez skrócenie okna sterowania przepływem strumienia do zera i późniejsze wznowienie pobierania. Oznacza to, że przeglądarka może pobrać podgląd lub pierwsze skanowanie obrazu, wyświetlić go i umożliwić innym pobieranie o wysokim priorytecie, a także wznowić pobieranie po zakończeniu wczytywania najważniejszych zasobów.

Push z serwera

Inną zaawansowaną funkcją HTTP/2 jest możliwość wysyłania przez serwer wielu odpowiedzi na jedno żądanie klienta. Oznacza to, że oprócz odpowiedzi na pierwotne żądanie serwer może przekazać klientowi dodatkowe zasoby (ilustracja 12–5), przy czym klient nie musi wyraźnie prosić o każde z nich.

Serwer inicjuje nowe strumienie (obietnice) dla zasobów push

Do czego potrzebny jest taki mechanizm w przeglądarce? Typowa aplikacja internetowa składa się z dziesiątek zasobów, z których wszystkie są wykrywane przez klienta podczas sprawdzania dokumentu dostarczonego przez serwer. W rezultacie może warto wyeliminować dodatkowy czas oczekiwania i pozwolić serwerowi na wcześniejsze przekazanie powiązanych zasobów. Serwer wie już, jakich zasobów będzie potrzebować klient – czyli push z serwera.

Jeśli zdarzyło Ci się wbudować w kod CSS, JavaScript lub inny zasób za pomocą identyfikatora URI danych (patrz Wstawianie zasobów), to masz już praktyczne doświadczenie w korzystaniu z komunikatów push z serwera. Ręcznie wbudowując zasób do dokumentu, w praktyce przekazujemy go do klienta, nie czekając, aż klient o niego poprosi. HTTP/2 pozwala osiągnąć te same rezultaty, ale przy większej wydajności. Zasoby push mogą być:

  • Pamięć podręczna przez klienta
  • Ponowne wykorzystywanie na różnych stronach
  • Multipleks obok innych zasobów
  • Priorytetowe według serwera
  • Odrzucone przez klienta

Poradnik PUSH_PROMISE

Wszystkie strumienie push z serwera są inicjowane za pomocą ramek PUSH_PROMISE, które sygnalizują intencję serwera przekazania opisanych zasobów do klienta. Należy je dostarczyć przed danymi odpowiedzi, które żądają przekazanych zasobów. Kolejność dostaw ma kluczowe znaczenie: klient musi wiedzieć, jakie zasoby ma przekazać serwer, aby uniknąć tworzenia zduplikowanych żądań dla tych zasobów. Najprostszym sposobem spełnienia tego wymogu jest wysłanie wszystkich ramek PUSH_PROMISE, które zawierają tylko nagłówki HTTP obiecanego zasobu, przed odpowiedzią elementu nadrzędnego (czyli ramki DATA).

Po otrzymaniu ramki PUSH_PROMISE klient może w razie potrzeby odrzucić strumień (za pomocą ramki RST_STREAM). Może się tak zdarzyć, np. gdy zasób jest już w pamięci podręcznej. To istotne ulepszenie w porównaniu z HTTP/1.x. Z kolei wykorzystanie wbudowywania zasobów, które jest popularną „optymalizacją” zasobów HTTP/1.x, jest równoważne z wymuszonym wypchnięciem: klient nie może z tego zrezygnować, anulować go ani przetworzyć samodzielnie.

W przypadku protokołu HTTP/2 klient zachowuje pełną kontrolę nad sposobem wykorzystywania powiadomień push z serwera. Klient może ograniczyć liczbę równoczesnych strumieni przekazywanych, dostosować początkowe okno sterowania przepływem, aby kontrolować ilość danych przekazywanych po pierwszym uruchomieniu strumienia lub całkowicie wyłączyć powiadomienia push z serwera. Ustawienia te są przekazywane za pomocą ramek SETTINGS na początku połączenia HTTP/2 i mogą być aktualizowane w dowolnym momencie.

Każdy przekazany zasób to strumień, który w przeciwieństwie do zasobu wbudowanego umożliwia jego indywidualne multipleksowanie, ustalanie priorytetów i przetwarzanie przez klienta. Jedynym ograniczeniem bezpieczeństwa egzekwowanym przez przeglądarkę jest to, że przekazywane zasoby muszą być zgodne z zasadą tego samego źródła – serwer musi mieć wiarygodność w przypadku dostarczanych treści.

Kompresja nagłówka

Każde transfer HTTP zawiera zestaw nagłówków opisujących przenoszony zasób i jego właściwości. W protokole HTTP/1.x metadane są zawsze wysyłane jako zwykły tekst i dodają od 500 do 800 bajtów narzutu podczas każdego transferu, a czasem nawet więcej kilobajtów, jeśli używane są pliki cookie HTTP. (patrz Pomiar i kontrolowanie nadmiarowości protokołu). Aby zmniejszyć te narzuty i zwiększyć wydajność, HTTP/2 kompresuje metadane nagłówków żądań i odpowiedzi za pomocą formatu kompresji HPACK, w którym zastosowano 2 proste, lecz skuteczne techniki:

  1. Umożliwia kodowanie przesyłanych pól nagłówka za pomocą statycznego kodu Huffmana, co zmniejsza rozmiar indywidualnego przesyłania.
  2. Wymaga, aby zarówno klient, jak i serwer obsługiwały i aktualizowały zindeksowane listy wcześniej widocznych pól nagłówka (czyli ustanawia wspólny kontekst kompresji), który posłuży później jako odniesienie do wydajnego kodowania wcześniej przesyłanych wartości.

Kodowanie Huffmana umożliwia kompresowanie poszczególnych wartości podczas ich przesyłania, a indeksowana lista wcześniej przesłanych wartości umożliwia nam kodowanie zduplikowanych wartości przez przekazanie wartości indeksu, które mogą służyć do skutecznego wyszukiwania i rekonstruowania kluczy i wartości pełnego nagłówka.

HPACK: Kompresja nagłówków dla HTTP/2

W ramach kolejnej optymalizacji kontekst kompresji HPACK składa się z tabeli statycznej i dynamicznej: tabela statyczna jest zdefiniowana w specyfikacji i zawiera listę typowych pól nagłówka HTTP, których mogą używać wszystkie połączenia (np. prawidłowych nazw nagłówków); tabela dynamiczna jest początkowo pusta i aktualizowana na podstawie wymiany wartości w ramach określonego połączenia. W efekcie rozmiar każdego żądania jest zmniejszany przez statyczne kodowanie Huffmana dla wartości, które nie występowały wcześniej, i zastępowanie indeksów wartościami, które są już obecne w tabelach statycznych lub dynamicznych po każdej stronie.

Bezpieczeństwo i wydajność HPACK

Wczesne wersje protokołów HTTP/2 i SPDY używały do kompresowania wszystkich nagłówków HTTP przy użyciu zlib z niestandardowym słownikiem. Zmniejszyło to o 85–88% rozmiar przesyłanych danych nagłówka i znacznie skróciło czas wczytywania strony:

W przypadku linku do DSL o niższej przepustowości (który ma link do przesyłania o szybkości zaledwie 375 kb/s), kompresja nagłówka żądania kompresji zwłaszcza doprowadziła do znacznego skrócenia czasu wczytywania strony w niektórych witrynach (czyli takich, które przesłały dużą liczbę żądań zasobów). Odkryliśmy skrócenie czasu wczytywania strony o 45–1142 ms tylko dzięki kompresji nagłówka. (dokument SPDY, chromium.org)

Latem 2012 r. opublikowano jednak „CRIME” atak bezpieczeństwa przeciwko algorytmom kompresji TLS i SPDY, który mógł doprowadzić do przejęcia sesji. W rezultacie algorytm kompresji zlib został zastąpiony algorytmem HPACK, który został stworzony po to, aby rozwiązywać wykryte problemy z bezpieczeństwem, był wydajny i prosty w prawidłowym wdrożeniu oraz oczywiście umożliwiał dobrą kompresję metadanych nagłówka HTTP.

Więcej informacji o algorytmie kompresji HPACK znajdziesz w artykule IETF HPACK – Header Compression for HTTP/2 (Kompresja nagłówków dla HTTP/2).

Więcej informacji