Bardzo mnie denerwuje powolne się wczytywanie stron internetowych. Główny narzut czasowy stanowi zazwyczaj spora liczba obiektów do załadowania wraz ze stroną serwisu. Każdy obiekt powoduje wywołanie kolejnych zapytań HTTP. Efektem końcowym dla strony jest powolne wczytywanie zawartości.
Jak zminimalizować liczbę zapytań HTTP?
W czym problem?
Problem jest dość banalny. Im więcej wykonywanych jest zapytań HTTP, tym strona dłużej się wczytuje. Sprawa się jednak komplikuje, gdyż przeglądarki mają pewne ograniczenie – uniemożliwiają pobieranie wielu plików z tej samej domeny w tym samym czasie. W większości przeglądarek ograniczenie dotyczy dwóch różnych obiektów (plików).
Specyfikacja HTTP/1.0 umożliwia pobieranie maksymalnie 4 plików w jednym czasie z jednego hosta. HTTP/1.1 ogranicza już tylko do 2 plików.
W przypadku gdy strona musi wczytać więcej niż dwa obiekty, proces wygląda następująco: wczytywane są dwa pierwsze pliki i przeglądarka czeka na pobranie tych plików (nie nawiązuje kolejnych zapytań HTTP). Gdy zapytanie zostanie wykonane, pobierany jest kolejny plik, etc. Powoduje to pewne ograniczenie, gdyż użytkownicy szybszych łączy mogliby prędzej pobrać wszystkie obiekty, jednak są ograniczani przez ilość połączeń wykonywanych do jednej domeny. Jak to rozwiązać? O tym za chwilę. Tymczasem prezentuję kilka sposobów minimalizacji zapytań HTTP.
CSS Sprites
Metoda ta polega na łączeniu wielu plików graficznych w jeden większy, a następnie za pomocą CSS (pozycjonowania) wyświetlamy konkretne obszary grafiki bazowej. Dzięki temu zamiast wykonywać kilkanaście zapytań, możemy wykonać jedno zapytanie, a następnie odpowiednio pozycjonować pożądane obszary grafiki bazowej.
Image Maps
Technika ta polega na utworzeniu jednego dużego obrazu, a następnie za pomocą specjalnego elementu HTML (map) ustawiane są obszary, do których można przypiąć konkretne zdarzenia i akcje (odsyłacze). Jest to mniej wygodna technika minimalizacji zapytań HTTP, aczkolwiek czasem przydatna i ułatwiająca życie webmastera.
Kodowanie małych grafik
Kolejną metodą minimalizacji zapytań jest kodowanie niewielkiej wielkości grafik przy pomocy algorytmu base64, a następnie podstawianiu do elementów IMG HTML.
Wygląda to następująco:
1 | <img src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAADUUlEQVQ4ywXByU+cdRzA4c+7zPu+IFURFIqV0rwUIhqMpmriwWBsJaVNE2PsBkZLYuwf0HCyB88eDNE0sdakRr1pvFpr0qQk1iWYli7ADCNIpQPTGZjl927zLl+fR0srqYgDsS2ILmhxhCkaDytVCsVVtBS6e55i78BebCtH2ArQdR3LzpGkCWb2uE4iGQmCrcHN+Vv8MXeDrfJD9o+MUK5WuHLlZ0SE4yffZWpqEhEhTRIky9AqWSKGCJnyuP7LVR78+x9xAprdzoFXXsVTO1z+5jKFwhKlUokjRw4zM3OOvr4+LMtCK0skbYkwf20Ox7QpN33+XFym7IV4QUinhGysr1LI5+nr7eHM9Bnu3l5gZuYcpmmiebEn+fk7tHY8KirgXrPOWpKwZ+QFGts1Jl8c5uknHuO7b79nafEu7sAAge/R3/8Mp06ewjRTYe7qNWI/pWd0lOWmouO5UZbqEc0tRabr6Lkc733wPpe+vEixuMJvc9cJPJ8Tx09gbmxX+XvxHrt7B/jrxu88+cYYG0GKV0/wSz5JCl4QIWnG6alJDo6NUVwuYFsmC7cWMO/kVyiWNqjHGmFXF5ZqUot8/K2INtWi2lCgCZnoJIHH6Esvs7pSJIhC1tbWMZ0wobpZIosT3E6Hj8dfJ9YMklbKtlJ8+sNPlOIOIh6lI2yQ1lu00oicqRMYOqbruqhmkyAKCFshvbs6aAQBtJs0vIzNuIN1lSOOW3QFivTBBnqakGUaruui9+3ZTVd3J77vs1IocPHSV7TbDo16nSwFL3YIQ8EIfNpbTe7fvoljGDw7PMzI0BC6oet8dPYsnlKIpHxy/jxffD5L4PmE9Rp2s0qX2qI/rfDPrz9ixgotE6anPyRnW6C8mihVk6NHJ8Rus0Q3dDEtW9yhYRkbn5ADx06L+9q4OJ194pi2dFqmvDNxWBrKk2qUCUkaSCv2pbR5Xw4delNM0xDDMETXTbEsRyxTF1vXpPuRNum0DHn7rYNSLpXEbyWiEhGUtyNpGkoQNqVer8rs7GcyNLhfHMsW28qJZeiyy9LleXeffH3hguzUaqLCliRZJL7aEi1OA8myDA1ANLJU0DSN4soq+XyeUDSG3H0MuoOYThtRJuQMcKSJkTX4H3Ky8cRV8/WFAAAAAElFTkSuQmCC" alt="BMW" > |
Przykład:
Dzięki takiemu rozwiązaniu nie trzeba wykonywać kolejnych zapytań HTTP do zewnętrznych zasobów. Wiąże się z tym jednak problem innej natury: obrazki tworzone w ten sposób nie będą buforowane przez przeglądarkę!
Przy pomocy tej metody można również tworzyć favicon i inne rodzaje grafiki. Najlepiej jednak nie przesadzać z tym sposobem :-) Więcej informacji w RFC 2397.
Złączenia plików JavaScript / CSS
Często programiści i webdeveloperzy dołączają do strony wiele różnych plików CSS i jeszcze więcej plików JavaScript. Powoduje to wykonanie niezliczonych ilości zapytań HTTP.
Przykład z życia wzięty (nasza-klasa.pl):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | <link rel="stylesheet" type="text/css" href="http://0.static.nk-net.pl/style/main:1599"> <link rel="stylesheet" type="text/css" href="http://0.static.nk-net.pl/style/sledzik:71b0"> <link rel="stylesheet" type="text/css" href="http://0.static.nk-net.pl/style/popup:709d"> <link rel="stylesheet" type="text/css" href="http://1.static.nk-net.pl/style/home:ac98"> <link rel="stylesheet" type="text/css" href="http://1.static.nk-net.pl/style/blog/main_page:9ca2"> <link rel="stylesheet" type="text/css" href="http://0.static.nk-net.pl/style/allegro:4f78"> <link rel="stylesheet" type="text/css" href="http://1.static.nk-net.pl/style/szukaj:c814"> <link rel="stylesheet" type="text/css" href="http://1.static.nk-net.pl/style/ratings:5172"> <link rel="stylesheet" type="text/css" href="http://1.static.nk-net.pl/style/avatar:9e6e"> <link rel="stylesheet" type="text/css" href="http://1.static.nk-net.pl/style/gifts:0006"> <link rel="stylesheet" type="text/css" href="http://1.static.nk-net.pl/style/btab:d813"> <link rel="stylesheet" type="text/css" href="http://0.static.nk-net.pl/style/widgets/widgets:420c"> <link rel="stylesheet" type="text/css" href="http://0.static.nk-net.pl/style/quick_menu:58f9"> <script type="text/javascript" src="http://0.static.nk-net.pl/script/mootools:9d62" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/mootools-more:80e0" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/manual_login_stats:76a6" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/misc:635b" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/misc2:3c10" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/nk_window:8ac1" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/menu/menu:9e1f" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/swfobject:0168" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/tw-sack:f213" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/opacity:5328" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/are_friends:f6a0" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/avatar/avatar2:3d61" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/avatar/avatar_options:73d8" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/avatar/generic_avatar:ce3a" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/mail_invite:9caa" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/mootools_misc:107d" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/blog/normal_box:df91" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/blog/promo_box:4c33" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/popup:3810" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/ajax_form:aa5a" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/sledzik/sledzik_misc:9c46" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/main_logged_in:111b" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/last_photos:6da2" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/avatar/might_know_avatar:7cbe" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/might_know/might_know:2156" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/might_know/might_know_info_box:99be" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/paginator:1d76" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/sledzik/widgets/sledzik_widget_shout_sender:ab92" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/storage:23cb" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/sledzik/shout:c7a9" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/sledzik/sledzik_observer:a558" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/sledzik/sledzik_controller:73dd" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/sledzik/sledzik_shout_box:6d63" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/sledzik/sledzik_shout_stars:9dea" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/avatar/sledzik/sledzik_avatar_interface:dab6" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/avatar/sledzik/sledzik_followee_avatar:94d9" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/avatar/sledzik/sledzik_promoted_avatar:72f0" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/sledzik/sledzik_boxs:9488" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/sledzik/sledzik_box_initializer:d5f6" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/overlib:156c" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/events_controller:492b" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/nktalk/nktalk_watched_events:c9f6" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/tab_updater:c004" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/blinking_tab/blinking_tab:f50c" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/forum_school_tab:5595" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/widgets/widget:5ff3" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/widgets/widgets_box:f2d1" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/mails/mails_observer:4a9a" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/menu/quick_menu/quick_menu_modules:7ac7" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/menu/quick_menu/quick_menu:cc98" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/url_validator/url_validator:199d" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/url_validator/url_filters:0a64" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/url_validator/url_parser:55d1" charset="UTF-8"></script> <script type="text/javascript" src="http://1.static.nk-net.pl/script/players/player_plugins:00b0" charset="UTF-8"></script> <script type="text/javascript" src="http://0.static.nk-net.pl/script/players/player:f7d0" charset="UTF-8"></script> |
Jak widzisz, sprawa nie wygląda za ciekawie. Niemniej jednak poważny webmaster nie dopuszcza do takich sytuacji, a szanuje swoich użytkowników i ich czas!
W celu optymalizacji strony należy ograniczyć liczbę pobieranych obiektów. W tym celu można połączyć wiele plików JavaScript / CSS w jeden duży (co później pozytywnie odbije się przy kompresji kodu po stronie serwera, o czym innym razem).
Across Domains
Metoda ta polega na tworzeniu wirtualnych serwerów dla domeny głównej, dzięki czemu możemy ominąć zasadę pobierania tylko dwóch plików jednocześnie! Bowiem tworząc nowe subdomeny dla wczytywanych obiektów nie ograniczamy się do jednej domeny, a dwóch lub więcej. Więcej na ten temat znajdziesz tutaj: Maximizing Parallel Downloads.
Przykład:
1 2 3 4 | <script type="text/javascript" src="http://www.static1.adres-strony.pl/scripts/jquery.js"></script> <script type="text/javascript" src="http://www.static2.adres-strony.pl/scripts/init.js"></script> <script type="text/javascript" src="http://www.static1.adres-strony.pl/scripts/tabs.js"></script> <script type="text/javascript" src="http://www.static2.adres-strony.pl/scripts/validate.js"></script> |
Podsumowanie notki
Jak widać, minimalizacja zapytań HTTP nie jest trudnym zadaniem. Wystarczy niewielka ilość czasu, a średniej wielkości serwis możemy odciążyć o kilkadziesiąt % transferu. Moim zdaniem jest to bardzo opłacalne.
Za optymalizacją wydajności serwisów powinna przemawiać również korzyść ze strony wyszukiwarek, gdyż Google faworyzuje strony wczytujące się w krótszym czasie.
Także ze strony biznesowej musimy dążyć do optymalizacji serwisu. Z badań nad użytecznością serwisów wynika, iż użytkownik jest w stanie czekać do 5 sekund na rozpoczęcie wczytywania strony (Web Page Rendering Should Be Kept to No More than Four Seconds). Jeśli nie będzie widział większych postępów, opuści stronę i przejdzie do konkurencji.
Już wkrótce postaram się więcej napisać o optymalizacji serwisów internetowych (m. in. Content Delivery Network, Cache-Control Header, ETags, Gzip Components).
Czyżby inspiracja bezowocną dyskusją na GL?
Pominąłeś dość ciekawe zagadnienie, mianowicie – gdy pobierany jest JS, to nic więcej nie jest pobierane.
(Może coś się zmieniło ostatnio, ale jakieś pół roku temu FF jeszcze tak robił, na innych przeglądarkach nie chciało mi się testować).
A teraz do sedna – miały być praktyczne porady, kody, itp. a tu teoria.
Jestem ciekaw jak to realizujesz (szczególnie łączenie i kompresję CSS/JS).
A – zapomniał bym – też chcę obrazek zamiast białego pampra ;)
Faktycznie, miało być dużo kodowania, a mało gadania. Ok, dziękuję za uwagę i motywację, w następnych postach będzie więcej kodu :-)
Btw. obrazki do komentarzy są pobierane z serwisu gravatar.com – w ten sposób rozwiązuje to WordPress.
No to czekam z niecierpliwością na nowy wpis – tym razem z kodem :)
Piszesz o tworzeniu subdomen w celu zwiększenia ilości plików możliwych do pobrania w tym samym czasie. Kiedy jednak bardziej skuteczne okazuje się trzymanie niektórych rodzajów plików na zupełnie innej domenie?
@Marta: podobne pytanie padło kilka dni temu na forum.php.pl – http://forum.php.pl/index.php?showtopic=153204 :-)
Czytałam już temat na forum, ale skupiają się tam na różnych subdomenach w obrębie tej samej domeny, a mnie nie do końca o to chodzi.
Więc chodzi o Content delivery network? Technika ta polega właśnie na przechowywaniu danych statycznych na wielu zewnętrznych serwerach, w sposób duplikowany i bieżąco aktualizowany, zlokalizowany możliwie blisko użytkownika końcowego?
Generalnie, chyba największą zaletą takiego rozwiązania jest możliwość przechowywania plików statycznych na serwerach z minimalną konfiguracją (przez co szybciej działających). Bez ładowania wszystkich modułów od proxy, przepisywania linków, wirtualnych hostów, autoryzacji, cache, deflate, itp itd.
A czasem na zupełnie innym serwerze, o czym wspomniał erix we wspomnianym temacie na forum PHP.pl (nginx = static, apache = dynamic).
Nie chcę się za bardzo wypowiadać o tym temacie bo dopiero uczę się HTMLa ale informacje na pewno mi się przydadzą bo również bardzo mnie denerwują wolno wczytujące się strony, zresztą na pewno każdego denerwują :) Pozdrawiam