Jak můžeme zrychlit své webové aplikace? 2. díl

Tento díl je pokračováním seriálu a technikách umožňující zrychlení načítání webových stránek a aplikací. V předchozím článku Jak můžeme zrychlit své webové aplikace? byly zmíněny potřeby minimalizovat HTTP požadavkysnižovat velikost externích souborů – například využitím CSS sprites nebo minimizací externích souborů.

V tomto článku se pokusím ukázat výhody a způsoby implementace ukládání dat do mezipaměti prohlížeče a komprese obsahu webu. Popisované způsoby navazují na předchozí článek.

Cache

Často si můžete povšimnout, že se při opakované návštěvě web načetl rychleji než v případě první návštěvy.

Data se při první návštěvě uložila do mezipaměti prohlížeče, tzv. cache, a při opětovné návštěvě se nemusela ze serveru opět stahovat (neuložený nebo aktualizovaný obsah se opět stáhl ze serveru). Díky tomu je možné snížit počet HTTP požadavků, ale také snížit celkový objem přenášených dat.

Jak to funguje

Aby se data správně ukládala a aktualizovala, tzv. invalidovala, je potřeba správně nastavit hlavičky požadavků. Ty se poté využívají při komunikaci pro ověření platnosti dat přes HTTP. Pokud nejsou hlavičky nastavené, uložení dat bude pouze dočasné v rámci sezení.

Osobně nepoužívám možnost zpracování dat v mezipaměti pomocí HTML tagů a proto tuto možnost nezmiňuji. Nepřijde mi příliš spolehlivá a hlavičky odeslané serverem je mohou přepsat.

HTTP hlavička požadavku na soubor styly.css
HTTP hlavička požadavku na soubor styly.css

Jak můžete vidět na obrázku, při zasílání požadavků pro stažení souboru ze serveru obsahuje hlavička několik parametrů (Hlavičky požadavků):

  • Cache-Control,
  • If-None-Match,
  • If-Modified-Since.

Ty server vyhodnotí a odešle na ně odpověď (Hlavičky odezvy):

  • Expires,
  • Etag,
  • Cache-Control.

V hlavičce odezvy Cache-Control jsou další parametry, které říkají, jak se souborem v mezipaměti zacházet:

  • Max-age – doba platnosti souboru, tzv. čerstvost. Po jejím uplynutí bude vyslán požadavek na jeho stáhnutí.
  • Public – data budou „cachována“ v rámci všech cache (i když budou data v zabezpečené části).
  • Private – opak parametru public (určíme, že data jsou privátní a nebudou se nijak ukládat).
  • No-cache – platnost dat se bude ověřovat vždy při vytvoření požadavku – před samotným stažením souboru se ověří jeho platnost.
  • Must-revalidate – pokud potřebujeme soubory ověřovat při každém zobrazení stránky a vždy při jejím načtení se provedou všechna nastavená ověřovací pravidla.
  • Proxy-revalidate – stejné jako must-revalidate, akorát v rámci proxy cache.

Příklad využití parametrů

Pokud potřebujeme ověřit platnost dat uložených v mezipaměti, využijeme k tomu parametry If-None MatchEtag, který obsahuje hash obsahu souboru. A který se využije k porovnání mezi hlavičkami. Tento mechanismus je řízen serverem automaticky.

Hlavička If-Modified-Since kontroluje, zda byla data od uvedeného data nějak upravena. Jak ukazuje příklad na obrázku, pokud byl soubor upraven od data 18. října 2010 22:35:18, data se invalidují a budou opět stažena.

Pokud bychom chtěli zabránit neustálému ověřování dat v mezipaměti, což samozřejmě také prodlužuje dobu načtení webu, může být vhodné pro jednotlivá data nastavení času, po který se nemusí platnost dat ověřovat. To je možné díky hlavičce Expires. Může však dojít k problémům kvůli špatné synchronizaci času na serveru a proto je vhodnější využít ke kontrole Etag.

Stavové kódy v hlavičce

Pokud budou všechny hlavičky platné, odpověď pro data bude stav 304 Not Modified a data se načtou z mezipaměti. Pokud by se ale soubor změnil a některá z hlaviček by se vyhodnotila jako neplatná, odpovědí bude stav 200 OK a soubor se opět stáhne ze serveru.

Pro lepší pochopení stavových kódu a parametrů v hlavičkách jsem přiložil jednotlivé situace, které mohou při požadavcích vzniknout:

První návštěva uživatele na webu (mezipaměť neobsahuje žádná data), stav je 200 OK a data se budou stahovat
První návštěva uživatele na webu (mezipaměť neobsahuje žádná data), stav je 200 OK a data se budou stahovat

Při první návštěvě nejsou v mezipaměti uložena žádná data a proto není možné žádnou hlavičku porovnat. Všechny požadované soubory budou staženy a uloží se do mezipaměti pro případ další návštěvy:

Opakovaná návštěva na stejném webu o několik dní později. Během té doby byl na serveru upraven soubor styly.css
Opakovaná návštěva na stejném webu o několik dní později. Během té doby byl na serveru upraven soubor styly.css

Při první návštěvě se data uložila do mezipaměti. Před další návštěvou byl na serveru upraven soubor se styly.css (změnil se jeho obsah) a pro který byla nastavena pravidelná kontrola jeho platnosti pomocí must-revalidate.

Při opětovné návštěvě stejného uživatele dojde k vytvoření požadavku na kontrolu data v mezipaměti. Pomocí hlavičky If-None-Match dojde ke zjištění, že soubor styly.css byl změněn, a bude opět stažen (ostatní se nahrají z mezipaměti).

Opakovaná návštěva na stejném webu o několik dní později. Žádný ze souborů nebyl na serveru změněn.
Opakovaná návštěva na stejném webu o několik dní později. Žádný ze souborů nebyl na serveru změněn.

Před další návštěvou webu nedošlo k úpravě žádného souboru. Data v mezipaměti prohlížeče se tedy vyhodnotí pomocí If-None-Match a Etagu jako platná načtou se právě z mezipaměti.

Díky tomuto mechanismu lze velmi efektivně snížit čas potřebný pro nahrávání webu a snížení celkového objemu stahožených dat – stáhnou se vždy až v případě, kdy bude třeba. Tento způsob je velmi vhodné využívat v případě mobilních webů.

Nastavení na webovém serveru

Aby vše správně fungovaly a hlavičky obsahovaly potřebné parametry, je potřeba vše nastavit na webovém serveru. Jako příklad uvedu nastavení pro Apache 2 s využitím modulů mod_headermod_expires.

Poznámka: Pokud máte webový server bez těchto modulů, požádejte administrátora, aby vám je pro váš web zpřístupnil.

Vše je pak řízeno nastavením v souboru .htaccess umístěném v kořenovém adresáři webu. V něm je dobré zapnout direktivu pro nastavení expirace:

<IfModule mod_expires.c>
### aktivace modulu mod_expires
ExpiresActive On
</IfModule>

Pro cachování jednotlivých souborů se pak použíjí pravidla s regulárním výrazem (porovnání podle přípony):

<IfModule mod_expires.c>
### aktivace modulu mod_expires
ExpiresActive On

<FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">
Header set Cache-Control "max-age= 2592000, public"
</FilesMatch>
</IfModule>

Tímto zajistíme, že všechna statická data, např. obrázky, javascriptové a CSS soubory budou uložena po dobu 30 dní v mezipaměti.

<IfModule mod_expires.c>
### aktivace modulu mod_expires
ExpiresActive On

<FilesMatch "\.(html|htm)$">
Header set Cache-Control "max-age=7200, must-revalidate"
</FilesMatch>
</IfModule>

Naopak tímto zajistíme, že se budou HTML a HTM soubory ukládat po dobu 2 dní s tím, že při každém požadavku dojde k ověření jejich čerstvosti. Pokud se na stránce něco změní, data se opět stáhnou.

Můžete také využít kratšího zápisu s řízením podle typu souborů, pro obrázky:

<IfModule od_expires.c>
### aktivace modulu mod_expires
ExpiresActive On

ExpiresByType image/gif image/jpg image/jpeg image/png "access plus 2 month"
</IfModule>

Pokud vás zaujala možnost ukládání dat do mezipaměti, pro detailní popis a možností cache doporučuji přečíst Kešovací návod na stránce jakpsatweb.cz.

Komprimace obsahu

Protokol HTTP 1.1 dovoluje přenášet data v komprimované podobě, díky čemuž je možné snížit objem přenášených dat a zvýšit rychlost načítání webu. Vše opět vychází z nastavení webového serveru, kde je obsah před odesláním komprimován a odeslán do prohlížeče, který jej již zpracuje.

Dnešní webové prohlížeče nemají s touto komunikací žádné problémy. Tento způsob přenosu obsahu je velice vhodný především pro mobilní weby, neboť umožní snížení velikosti souborů až o desítky KB.

Hlavička požadavku od prohlížeče a následná odpověď serveru – data jsou odesílána v komprimované podobě
Hlavička požadavku od prohlížeče a následná odpověď serveru – data jsou odesílána v komprimované podobě

Na obrázku jsou zachyceny hlavičky požadavku a hlavičky odpovědi. Prohlížeč nejprve serveru řekne, v jaké podobě je schopen komprimovaná data přijmout (Accept-Encoding) a v odpovědi je, že odeslána data jsou komprimovaná (Content-Encoding). V tomto případě je to pomocí metody gzip.

Jak ukazují následující dva obrázky, pomocí komprimace obsahu je možné velikost dat znatelně snížit:

Stahovaná data ze serveru bez komprese – celkem přeneseno 284,1 KB
Stahovaná data ze serveru bez komprese – celkem přeneseno 284,1 KB

Na obrázku je zachycen stav stažení souborů ze serveru, kde nebyla zapnutá žádná komprese. Celkový objem přenesených dat byl 284,1 KB. Všimněte si především knihovny jquery-1.4.2.min, která má velikost 70,5 KB.

Stahovaná data ze serveru s kompresí – celkem přeneseno 222,8 KB
Stahovaná data ze serveru s kompresí – celkem přeneseno 222,8 KB

Na dalším obrázku můžete vidět snížení přenášeného objemu dat o 61,3 KB díky zapnuté kompresi. Jak si můžete povšimnout, velikost knihovny jquery-1.4.2.min je nyní 24 KB.

Příklad je demonstrován na malé webové stránce s neoptimalizovanými obrázky (mají velkou velikost) a na které komprese nefunguje. Pro obrázky je potřeba další optimalizace (více informací v článku Jak můžeme zrychlit své webové stránky? 3. díl).

Podobná situace jako je u obrázků se týká i dalšího obsahu, například videa nebo dalších multimediálních souborů. U těch už byla nějaká komprese většinou provedena (i když je jejich velikost stále značná) a další komprese na ně nemá vliv.

Kompresi je tedy vhodné využívat pro všechna „textová“ data, např. HTML, TXT, JavaScript, CSS nebo JSON.

Poznámka – Jak jsem podotknul, značně se snížila velikost souboru jquery-1.4.2.min. z 70,5 KB na 24 KB. Často jsem četl připomínky, že velikost udávaná na oficiálním webu jQuery pro produkční verzi je špatná, ale že se pohybuje právě kolem 70 KB. Plno lidí si neuvědomilo význam dodatku – velikost minifikované verze při použití Gzip komprese.

Nastavení na webovém serveru

Aby vše správně fungovalo a hlavičky obsahovaly potřebné parametry, je potřeba vše nastavit na webovém serveru. Jako příklad uvedu nastavení pro Apache 2 s využitím modulu mod_deflate.

Poznámka: Pokud máte webový server bez tohoto modulu, určitě požadujte jeho zapnutí na administrátorovi serveru.

<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/x-javascript text/javascript application/javascript application/json
</IfModule>

Touto direktivou nastavíme komprimaci obsahu pro většinu textových dat.

Problém by mohl nastat, pokud by návštěvník webu použil prohlížeč, který nepodporuje HTTP 1.1 (např. starší verze prohlížeče NetScape 4). Pro tyto případy je nutné nastavit zvlášť celý modul, avšak pro toto nastavení je doporučené pečlivě prostudovat dokumentaci modulu mod_deflate.

Jak vypadá komprimovaný obsah?

Podoba odeslané HTML stránky bez zapnuté komprese
Podoba odeslané HTML stránky bez zapnuté komprese

Pokud bychom na serveru neměli zapnutý mod_deflate, stránka by se odeslala v klasické podobě.

Podoba odeslané HTML stránky se zapnutou kompresí
Podoba odeslané HTML stránky se zapnutou kompresí

Jak ukazuje obrázek, klasický obsah HTML souboru byl změněn na nepřeložitelnou změť znaků, se kterou si však prohlížeč poradí a data převede zpět do správné podoby.

Pokračování v dalším článku

V článku Jak můžeme zrychlit své webové aplikace? 3. díl dokončuji možnosti pro zmenšení přenášeného objemu dat díky správnému využití obrázků a jejich optimalizaci.

Komentáře

  1. Moc pěkně napsané shrnutí. Díky gzipu a kešování se dá opravdu ušetřit neuvěřitelné množství trafficu a to se může u některých hostingů rovnat slušnému snížení výdajů.
    Šup do čtečky 🙂

  2. no az na to ze po zapnuti gzipu/deflate nikdy server neposle hlavicku 304 – aby si prohlizec vzal stranku z cache..takze to usetri trafic akorat u menicich se stranek. Zkouseno na Apache..treba google to ma OK

  3. korny: mně se to nikdy nepodařilo zreplikovat a vše funguje správně – i při komprimovaném obsahu se mi odesílá hlavička 304.

Napsat komentář: korny Zrušit odpověď na komentář