Lemat, strona prywatna

Jak keszować strony tworzone przez PHP

PHP ze swej natury służy do generowania stron o charakterze dynamicznym. Zdarza się jednak (tak, jak na tej stronie), że podstrony danego sajtu są tworzone i od czasu do czasu modyfikowane, na tyle jednak rzadko, że użytkownik podczas jednej sesji nie zobaczy zmian.

Warto zatem pozwolić przeglądarce zapisać zawartość takiej podstrony na dysku, aby przy kolejnym jej wywołaniu nie ściągała jej z serwera i tym samym nie marnowała czasu i łącza.

Przypuśćmy, że w $page_data mamy datę ostatniej zmiany na stronie (czerpaną z bazy lub czas modyfikacji pliku) w formacie unixowego timestampa:

$last_mod=gmdate('D, d M Y H:i:s',$page_data). ' GMT';
header("Pragma: "); // session_start daje "no-cache" więc trzeba wyczyścić.
header("Cache-Control: max-age=3600, must-revalidate");
header('Last-Modified: ' . $last_mod);
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 3600) . ' GMT');
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) 
	&& trim($_SERVER['HTTP_IF_MODIFIED_SINCE'])==$last_mod) {
		header('HTTP/1.0 304 Not Modified');
		header('Content-Length: 0');
//		header('Status: 304 Not Modified');
// na serwerach home.pl nie działa HTTP/1.1, trzeba robić przez Status.
		die();
		}

Występująca powyżej dwa razy liczba 3600 sekund powinna zostać dobrana doświadczalnie tak, by była na tyle duża, aby użytkownik powracając na daną podstronę dostał ją z kesza, jednak na tyle mała, aby w przypadku modyfikacji zawartości podstrony mógł taką modyfikację jednak obejrzeć w niedalekiej przyszłości.

A teraz krótka analiza:
Przeglądarka wchodząc na stronę, na której już była i z której otrzymała nagłówek Last-Modified wysyła do serwera nagłówek IF_MODIFIED_SINCE. Porównując go z datą $page_data możemy stwierdzić czy musimy do przeglądarki wysłać całą treść, czy tylko wystarczy sam nagłówek HTTP/1.1 304 Not Modified, po którym możemy już nic nie wysyłać.

Jeżeli jednak już wysyłamy zawartość strony to można powiedzieć przeglądarce, że stronę można keszować (czyścimy Pragma: no-cache), że powinna ją keszować przez 3600 sekund (max-age), ale w zasadzie powinna za każdym razem sprawdzać, czy coś się zmieniło (must-revalidate).

Następnie jest nagłówek Expires, który jest w zasadzie sumą obecnego czasu oraz max-cache i też mówi przeglądarkom jak długo keszować.

Na końcu zaś nagłówek Last-Modified podający czas ostatniej modyfikacji strony - ten czas za każdym razem przeglądarka będzie nam odsyłać do porównania.

Bardziej szczegółowo zagadnienie keszowania jest opisane na tej stronie: http://www.mnot.net/cache_docs/
Można też sięgnąć do RFC http://www.ietf.org/rfc/rfc2616.txt rozdział 13, ale to ciężko strawny dokument. Oprócz tego w manualu do PHP, w komentarzach userów do funkcji header() można coś ciekawego znaleźć.

Można też wprowadzić drobną modyfikację:

header("Cache-Control: max-age=3600, must-revalidate, post-check=600, pre-check=1800");

w powyższym przykładzie przeglądarka przez 600 sekund będzie podawała dokument wyłącznie z kesza, następnie przez 1200 sekund będzie podawała dokument z kesza, ale też sprawdzała na serwerze czy jest nowsza wersja (i jeżeli jest to ją zaciągnie). Po upłynięciu 1800 sekund od pobrania przeglądarka uzna, że dokument najprawdopodobniej uległ zmianie i należy go pobrać z serwera.

Przydatnym narzędziem do sprawdzania nagłówków jest plugin WebDeveloper dla Firefoxa / Mozilli. Jest tylko jeden mankament - nigdy nie pokazuje nagłówków 304 Not Modified - zawsze pokazuje ostatnie 200 OK - przydałby się bardziej sniffer typu tcpdump, ethereal.

Jak się zachowują przeglądarki.

Firefox przy każdym odwołaniu do keszowanego dokumentu będzie wysyłał do serwera request i dostawał 304 aż dokument zostanie zmieniony.
IE przy każdym odwołaniu do keszowanego dokumentu będzie pobierał go z dysku aż do upłynięcia max-cache sekund. Wtedy połączy się z serwerem i dostanie 304.
Oczywiście jeżeli dokument zostanie usunięty z kesza przeglądarki będzie musiała się ona połączyć i pobrać go w całości.

Jest pewien konflikt pomiędzy IE a mod_deflate apacza:

  1. IE po wymuszeniu przez F5/Ctrl-F5 łaskawie pobierze z serwera nowszą wersję dokumentu
  2. przy ponownym wejściu na ten dokument ładuje poprzednią (starą) wersję - z dysku !!!
  3. wydaje mi sie, że taka ciuciubabka trwa aż do upłynięcia max-cache czasu. Wtedy IE łączy się z serwerem i dostaje nową stronę.

Oprócz dokumentów generowanych przez PHP można zatrudnić apacza do ustawiania odpowiednich nagłówków - odpowiada za to moduł expires:
LoadModule expires_module lib/apache/mod_expires.so

<IfModule expires_module>
ExpiresActive On
#ExpiresDefault "access plus 1 days"
#ExpiresByType text/html "access plus 1 minutes"
ExpiresByType image/gif "access plus 3 days"
ExpiresByType image/png "access plus 3 days"
ExpiresByType image/jpeg "access plus 3 days"
ExpiresByType application/x-javascript "access plus 3 days"
ExpiresByType text/css "access plus 3 days"
</IfModule>

Użyte tutaj ExpiresByType mówi apaczowi aby dodawał nagłówki keszujące do obrazków, plików .js i .css, można do tego dodać na przykład .pdf, .zip, .doc. Gdyby ExpiresDefault był odhaszowany toby wszystkie dokumenty serwowane przez apacza były keszowane przez 1 dzień - dla mnie jako programisty PHP to nieprzydatna właściwość.

Data utworzenia : 2005-10-11, data aktualizacji :2008-10-21

Skomentuj ten tekst

Komentarze:

kwesoly
2009-09-27 04:31:32
Podgladanie komunikacji
Do Firefoxa jest jeszcze dodatek Tamper Data - pozwala dokładnie prześledzić kolejne żądania klienta i odpowiedzi serwera.
AzMoDaN
2007-06-02 16:02:59
Opera
Wygląda na to że nie działa za bardzo w Operze. Zaczyna działać po dodaniu do nagłówka Status (czyli usunięciu komentarza dla wersji dla home.pl).
Adax
2006-11-12 14:58:45
PHPCache
Ja to robie tak, za pierwszym razem jak ktos wchodzi do mojej aplikacji PHP generuje strone i zapisuje ja w pliku html z czasem np 1 minuty, Kolejne osoby ktore wchodza w ciagu jednej minuty pobieraja juz gotowy wygenerowany kod. Nadaje sie to golownie do danych ktore sie szybko nie zmieniaja. Pozdrawiam
Sasek
2006-09-28 15:53:11
keszowanie php do html
Poczytaj o Zend Cache ;-J Możesz albo qpić ich rozwiązanie, albo zaimplementować to sam. Pokazują jak to działa. Sposób genialny w swojej prostocie - reagują na error 404 serwując error page jako skrypt php ;-) Jeśli strona nie istnieje, to skrypt ją generuje i niezauważalnie przekierowuje spowrotem do niej.
Odpowiedź Lemata:
wprawdzie "każdemu według potrzeb", ale ja jakoś nie widzę u siebie konieczności stosowania takiego softu
Przemek
2005-12-13 19:13:17
A jak zrobić keszowanie na serwerze?
Nie wiem nic o keszowaniu, ale przymierzam się do tego w przyszłości. Myślałem jednak raczej o tworzeniu gotowego kodu html na serwerze. Co prawda nie zaoszczędzi to transferu ale przyspieszy czas oczekiwania bo pominięte zostanie wykonanie skryptu i ewnt. zapytań SQL. Sterowanie wyobrażałem sobie w postaci krótkiego skrypciku, który sprawdzi czy od ostatniej generacji html coś się zmieniło - jeśli nie to zapoda gotowy html a jeśli się zmieniło to wygeneruje nowy. Nie byłoby wtedy problemu wyświetlania nieaktualnej zawartości. Ale nie wiem czy coś takiego się stosuje, to tylko taki pomysł.
Odpowiedź Lemata:
stosuje się, problem w tym, że jak ma się np. menu w formie drzewa, to powstaje na dysku wiele dokumentów HTML o takiej samej treści ale różniące się otworzeniem danej gałęzi menu (czyli szczegółem), co jest po prostu dyskożerne. Ja bym sugerował rozbicie HTMLa na tyle części funkcjonalnych ile potrzeba i składanie wynikowej strony z gotowych klocków.
wszystkie opinie »
Dlaczego nie używasz firewalla?
Protected by spf
[Nospam-PL.NET]
Seti@Home
www.php.net
© Lemat 2004 - ∞
Cookie Bullshit
Mapa strony
engine: lem.. at lemat·priv·pl