Kompresja

Kompresja Treści Internetowych

Techniki kompresji HTTP: gzip, Brotli i strategie optymalizacji przeglądarek.

Dlaczego kompresuje się zasoby webowe

Każda strona składa się z zasobów pobieranych z serwera WWW do przeglądarki. Należą do nich tekst, obrazy, arkusze stylów definiujące wygląd strony oraz skrypty zapewniające interakcję. Po wybraniu adresu przeglądarka wysyła żądanie, odbiera odpowiedzi i układa zasoby w dokument, który może wyświetlić. Przeglądarka pobiera te zasoby w wielu żądaniach, a ich łączny rozmiar wpływa na czas pobierania i wyświetlania strony. Znaczenie mają także opóźnienie połączenia, dostępna przepustowość oraz czas przetwarzania po stronie serwera i klienta. opisuje te czasy oraz zachowanie strony podczas pobierania zasobów. W przypadku urządzeń mobilnych zmienna jakość łącza zwiększa znaczenie liczby przesyłanych bajtów.

Kompresja webowa zmniejsza ilość danych przesyłanych między serwerem a przeglądarką. Kompresja HTTP dotyczy reprezentacji przesyłanej przez sieć, natomiast kompresja obrazu zmienia plik obrazu. Serwer może skompresować zasób tekstowy, taki jak plik HTML, przed wysłaniem, a przeglądarka odtwarza jego pierwotną postać po odebraniu. Operacja jest bezstratna dla tekstu, dlatego parser otrzymuje te same znaki po dekodowaniu. Mniejszy transfer ogranicza zużycie przepustowości i może skrócić pobieranie strony. Kodowanie treści HTTP nie zastępuje optymalizacji samego zasobu, ponieważ osobno można zmienić jego format, zawartość lub liczbę żądań. Na wynik odczuwany przez użytkownika wpływa również kolejność dostarczania zasobów. Arkusz stylów może blokować pierwsze renderowanie, a skrypt może wymagać wcześniejszego pobrania. Kompresja zmniejsza transfer, lecz nie usuwa zależności między zasobami ani kosztu ich analizy w przeglądarce.

Negocjowanie kodowania HTTP

Kompresja HTTP wymaga współdziałania klienta i serwera. Ustalenia są przekazywane w . Klient opisuje akceptowane kodowania, a serwer wybiera reprezentację odpowiednią dla żądania. Proces wyboru reprezentacji treści przebiega następująco:

  1. Przeglądarka podaje akceptowane kodowania:

    Gdy przeglądarka żąda zasobu, na przykład głównego pliku HTML, dołącza nagłówek Accept-Encoding. Wartość nagłówka określa kodowania treści, które klient może odebrać. Klient może też podać preferencje, na przykład gzip;q=0.8, gdzie parametr jakości wpływa na wybór serwera.

    Accept-Encoding: gzip, deflate, br

    Przykład obejmuje kodowania gzip, deflate i br. Parametry jakości mogą określać preferencje klienta, lecz serwer wybiera kodowanie zgodnie z negocjacją i własną konfiguracją. Wartość jakości równa zero oznacza, że klient nie akceptuje danego kodowania. Brak odpowiedniego wariantu może prowadzić do wysłania treści bez kodowania.

  2. Serwer wybiera reprezentację:

    Serwer analizuje nagłówek Accept-Encoding i porównuje go z własną konfiguracją. Może skompresować pliki tekstowe, takie jak HTML i CSS. Zwykle pomija zasoby już skompresowane, na przykład JPEG i archiwa ZIP, ponieważ dodatkowy etap może zwiększyć rozmiar albo koszt przetwarzania.

  3. Serwer kompresuje i odpowiada:

    Jeżeli serwer wybierze kodowanie Brotli, pobiera żądany plik, na przykład index.html, i wysyła jego skompresowaną reprezentację. Nagłówek Content-Encoding wskazuje kodowanie zastosowane do treści odpowiedzi. Nie zmienia on typu zasobu, który nadal opisują między innymi typ MIME i semantyka odpowiedzi.

    Content-Encoding: br

    Wartość br informuje odbiorcę, że do dekodowania treści należy użyć Brotli. Gdy serwer wysyła treść bez kodowania, nagłówek może zostać pominięty.

    Jeżeli wariant odpowiedzi zależy od nagłówka żądania, cache powinien otrzymać także Vary: Accept-Encoding.

  4. Przeglądarka dekoduje treść:

    Przeglądarka odczytuje Content-Encoding: br i dekoduje strumień Brotli. Otrzymany kod HTML może następnie przetworzyć i wyrenderować.

Negocjacja pozwala serwerowi dobrać reprezentację zgodną z możliwościami klienta. Ten mechanizm działa dla każdego zasobu, którego odpowiedź podlega kodowaniu treści. Gdy odpowiedź jest przechowywana w pamięci podręcznej, wariant kodowany i niekodowany muszą być rozróżniane na podstawie nagłówka Vary. Kodowanie nakłada się na reprezentację przed wysłaniem, dlatego klient musi znać kolejność zastosowanych kodowań, jeżeli odpowiedź używa więcej niż jednego kodowania. Po stronie serwera warto przechowywać warianty razem z informacją o ich kodowaniu, aby cache nie zwrócił klientowi nieobsługiwanej reprezentacji.

Gzip i format DEFLATE

Gzip jest szeroko obsługiwanym kodowaniem HTTP opartym na bezstratnym algorytmie . DEFLATE łączy wyszukiwanie powtórzeń z kodowaniem statystycznym, dlatego nadaje się szczególnie do zasobów tekstowych. Format Gzip zawiera także informacje potrzebne do odczytania strumienia i kontroli jego integralności.

Proces Gzip: format DEFLATE

Gdy serwer kompresuje plik tekstowy, taki jak CSS lub JavaScript, za pomocą Gzip, wykonuje dwuetapowy proces:

  1. Wyszukiwanie powtórzeń metodą LZ77: Algorytm skanuje plik wejściowy i utrzymuje przesuwne okno ostatnio przetworzonych danych. Gdy znajdzie wcześniejszą sekwencję, zastępuje ją odwołaniem opisującym odległość i długość dopasowania. W DEFLATE odległość odwołania może sięgać 32 KiB, a długość dopasowania, maksymalnie 258 bajtów, jest kodowana osobno. Dane bez dopasowania są zapisywane jako literały. W plikach HTML, CSS i JavaScript powtarzają się między innymi znaczniki <div>, atrybut class= oraz słowo function.
  2. Kodowanie Huffmana: Wynik etapu LZ77 zawiera literały oraz odwołania. Kodowanie Huffmana analizuje częstość symboli i przypisuje im kody o zmiennej długości. Częste symbole otrzymują krótsze kody, a rzadkie dłuższe, co usuwa część pozostałej nadmiarowości statystycznej. DEFLATE używa jednego drzewa kodów Huffmana dla literałów i długości oraz osobnego drzewa dla odległości, dzięki czemu oba rodzaje informacji mogą mieć własne rozkłady kodów.

Rozmiar wyniku Gzip zależy od treści, jej powtarzalności i ustawień kodera. Włączenie tego kodowania po stronie serwera zmniejsza transfer zasobów tekstowych bez zmiany ich treści po dekodowaniu. Dla zasobów zmienianych rzadko można przygotować skompresowany wariant wcześniej, aby koszt kodowania nie występował przy każdym żądaniu. Przy zmianie pliku wariant trzeba ponownie utworzyć, aby klient nie otrzymał nieaktualnej reprezentacji. Format Gzip ma nagłówek identyfikujący strumień oraz stopkę zawierającą CRC-32 i rozmiar danych wejściowych modulo 2^32. Te pola pozwalają wykryć część uszkodzeń i sprawdzić długość danych po dekompresji.

Brotli i słownik statyczny

Opracowany przez Google jest kolejnym kodowaniem treści stosowanym w HTTP. W wielu zasobach tekstowych może uzyskać mniejszy wynik niż Gzip, lecz koszt i wynik zależą od ustawień oraz danych wejściowych.

Słownik statyczny Brotli

Brotli korzysta z wyszukiwania powtórzeń oraz kodowania entropijnego, a jego ważnym elementem jest wbudowany słownik statyczny. Specyfikacja definiuje słownik o rozmiarze 122 784 bajtów, zawierający gotowe ciągi i ich formy. Dla wpisów słownika określono także dopuszczalne długości kopiowania od 4 do 24 bajtów. Słownik jest znany koderowi i dekoderowi, dlatego nie trzeba przesyłać go razem z każdym zasobem. Koder może odwołać się do takiego ciągu bez wcześniejszego wystąpienia w bieżącym pliku.

Gzip wyszukuje dopasowania w dotychczas przetworzonych danych. Brotli może dodatkowo odwoływać się do słownika statycznego, co pomaga przy krótkich zasobach z niewielką liczbą powtórzeń wewnętrznych. Słownik przechowuje także zdefiniowane formy ciągów, więc koder może dopasować fragment o innej długości bez umieszczania go wcześniej w pliku. Odwołania do gotowych ciągów zmniejszają liczbę danych potrzebnych do zapisania reprezentacji.

Wydajność i Kompromisy

Rozmiar wyniku Brotli zależy od treści oraz ustawień jakości. W porównaniu z Gzip może powstać mniejsza reprezentacja, co ogranicza liczbę bajtów przesyłanych do klienta. Porównanie ma sens przy tych samych danych wejściowych i porównywalnym poziomie jakości, ponieważ zmiana ustawień wpływa na wynik i czas kodowania. Przy wyborze wariantu trzeba uwzględnić czas pracy serwera, wielkość pliku oraz koszt dekodowania po stronie klienta.

Wyższe ustawienia jakości mogą zwiększyć pracę kodera Brotli. Serwer może więc przygotować wcześniej skompresowane warianty statycznych zasobów i wysyłać je zgodnie z negocjacją kodowania. Wariant należy przechowywać razem z informacją o zastosowanym kodowaniu i używać go tylko dla klienta, który je akceptuje. Odbiorca wykonuje dekodowanie po swojej stronie, a koszt tego etapu jest niezależny od czasu potrzebnego na przygotowanie wariantu przez serwer.

Optymalizacja zasobów poza transferem

Gzip i Brotli zmniejszają reprezentację przesyłaną przez sieć. Optymalizacja może zmniejszyć zasób zanim serwer zastosuje kodowanie HTTP. Obie warstwy uzupełniają się podczas dostarczania strony.

Minifikacja kodu

to proces, który dotyczy zasobów tekstowych, takich jak HTML, CSS i JavaScript. Programiści piszą kod z dużą ilością białych znaków, komentarzy i długich, opisowych nazw zmiennych, aby był on czytelny i łatwy w utrzymaniu dla ludzi. Jednak przeglądarka internetowa nie potrzebuje niczego z tego, aby wykonać kod.

Narzędzia do minifikacji analizują kod i usuwają znaki zbędne dla parsera, takie jak białe znaki i komentarze. Mogą także skracać nazwy lokalnych zmiennych i funkcji, jeżeli nie zmienia to interfejsu zasobu. Nie powinny usuwać znaków, które rozdzielają tokeny albo zmieniają znaczenie kodu. Zminifikowany plik jest krótszy, lecz zachowuje funkcjonalność wymaganą przez przeglądarkę. Mapy źródeł pozostają osobnym zasobem używanym podczas debugowania. Mniejsza liczba powtórzeń może dodatkowo poprawić wynik kodowania Gzip lub Brotli.

Optymalizacja obrazów i czcionek

Nowoczesne formaty obrazów, takie jak WebP i AVIF, mogą ograniczyć rozmiar obrazu względem JPEG lub PNG przy porównywalnej jakości. Format WOFF2 oraz ograniczają liczbę danych w pliku czcionki. Subset może obejmować alfabet używany przez konkretną wersję językową strony, lecz musi zachować znaki potrzebne do wyświetlenia treści. Obrazy i czcionki są zwykle już skompresowane we własnym formacie, dlatego największy efekt daje ich przygotowanie przed wysłaniem.

Buforowanie i żądania warunkowe

Gdy użytkownik ponownie odwiedza stronę, przeglądarka sprawdza lokalny . Nagłówek Cache-Control określa, jak długo kopia może być przechowywana, między innymi za pomocą dyrektywy max-age. Świeża kopia pozwala użyć zasobu bez kolejnego żądania.

Po wygaśnięciu kopii przeglądarka może wysłać żądanie warunkowe z informacją o posiadanej wersji, na przykład za pomocą ETag albo Last-Modified. Serwer porównuje tę informację z bieżącą reprezentacją i może odpowiedzieć kodem 304 Not Modified, jeśli reprezentacja się nie zmieniła. Odpowiedź nie zawiera wtedy ponownie całej treści. Przeglądarka używa lokalnego pliku i nie pobiera całej treści ponownie. Połączenie buforowania z kompresją wymaga spójnego tworzenia identyfikatorów wersji dla wszystkich wariantów reprezentacji. Zmiana pliku powinna unieważnić poprzedni wariant, aby kolejne żądanie otrzymało aktualną treść i właściwe kodowanie.

Powiązane artykuły