wtorek, 12 czerwca 2012

Powrót po dłuższej przerwie

Po dłuższej przerwie blog Optymalizacja stron WWW wznawia swą aktywność. W kolejnych postach dokończony zostanie temat minimalizacji żądań http przez zastosowanie takich technik jak buforowanie danych z wykorzystaniem nagłówków Expires. Kolejnymi tematami będą: zmniejszenie wielkości przesyłanych danych, korzystanie z Content Delivery Network, optymalizacja grafiki na potrzeby stron internetowych, a także optymalizacja kodu CSS i JS. Omówienie tych tematów stworzy swego rodzaju kompendium wiedzy na temat podstawowych technik zwiększania wydajności stron internetowych. Zapraszam do lektury.

wtorek, 21 września 2010

Minimalizacja żądań http

Każde żądanie http wymaga nawiązania połączenia pomiędzy przeglądarką a serwerem na którym znajdują się pobierane elementy. Obsługa każdego żądania http wymaga dodatkowego czasu ze strony serwera oraz przesłania dodatkowych danych z przeglądarki na serwer i z powrotem. Dobrym rozwiązaniem jest więc minimalizacja ilości żądań http potrzebnych do wyświetlenia strony. Na szczęście istnieje kilka prostych technik, które pozwalają znacznie zredukować ich liczbę bez większych utrudnień w tworzeniu strony.

Przede wszystkim należy zlokalizować które elementy naszej strony odpowiedzialne są za większość żądań http potrzebnych do jej załadowania. Częstym winowajcą są tu pliki graficzne, w szczególności odpowiedzialne za elementy menu, czy innych części graficznego interfejsu użytkownika. Ilość tego typu plików jest zazwyczaj duża, a wielkość każdego z nich wręcz znikoma. Co za tym idzie ustanawianych jest mnóstwo żądań http tylko po to żeby przesłać drobny fragment treści znajdujących się na stronie.

CSS Sprite


Podstawową techniką zmniejszania ilości plików graficznych jest tworzenie tak zwanych CSS Sprite, czyli plików złożonych z wielu oddzielnych elementów graficznych. Dzięki temu wszystkie te elementy przesyłane są jako jeden plik, a co za tym idzie wykonywane jest jedno żądanie http. Przesłany plik może być następnie wielokrotnie wykorzystywany na stronie do wyświetlanie poszczególnych jego części stanowiących osobne grafiki. W tym celu wystarczy ustawić go jako obrazek tła odpowiedniego elementu np.: div, lub span i za pomocą parametrów background-position oraz width i height wybrać odpowiedni fragment.
Poniższy fragment kodu ilustruje użycie CSS Sprite
< div style=”background-image: url(‘csssprite.png’);
background-position: -100px -45px;
width: 30px; height: 30px;”> </ div >

Oprócz zalety w postaci redukcji ilości żądań http rozwiązanie to ma jeszcze jedną zaletę. Większe pliki w szczególności ułożone w poziomie zazwyczaj lepiej się kompresują niż ich części składowe osobno.

Minimalizacja odrębnych plików JavaScript i CSS


Tak samo jak w przypadku grafiki, również dodatkowy kod strony w postaci JavaScript i CSS może znajdować się w wielu plikach, a co za tym idzie wymagać wykonania wielu żądać http. Także w tym przypadku możemy w łatwy sposób zredukować ilość odrębnych plików koniecznych do załadowania strony. Z pewnością nie będzie to zgodne z zaleceniami programistów, w szczególności tych dbających o „piękno” tworzonego przez nich kodu, jednak w tym przypadku zamiast na „piękno” warto postawić na wydajność. Zarówno kod JavaScript jak i arkusze CSS powinny być połączone w możliwie jak najmniejszą ilość odrębnych plików. Należy jednak pamiętać, że w jednym pliku nie powinno się łączyć JavaScript z CSS. Szczegóły separacji tych dwóch elementów składowych stron internetowych oraz ich umiejscowienia w dokumencie HTML będzie jeszcze mowa przy innej okazji.

W idealnym przypadku każda strona internetowa powinna zawierać tylko jeden plik JavaScript i jeden plik CSS. Z praktycznego punktu widzenia jest to jednak zwykle trudne do osiągnięcia. Zazwyczaj różne podstrony tej samej witryny korzystają z różnych zestawów skryptów. Nie można pozwolić by każda strona ładowała wszystkie skrypty niezależnie od tego czy są jej potrzebne czy nie, gdyż znacznie pogorszy to wydajność i zwiększy ruch sieciowy. Z kolei przygotowywanie oddzielnych plików dla każdej podstrony w przypadku gdy korzystamy z wielu zestawów skryptów może być bardzo czasochłonne i stwarzać możliwość popełnienia błędów. Z tego względu często optymalnym rozwiązaniem jest pogrupowanie kodu w moduły które zawierają tej jego fragmenty, które najczęściej są wykorzystywane razem, a następnie dołączenie do danej strony jedynie potrzebnych modułów.

Buforowanie danych


Ilość żądań http może zostać również efektywnie zredukowana przez stosowanie nagłówków Expires, gdyż oznaczone nimi elementy, w wyznaczonym czasie nie będą pobierane z serwera, tylko z buforu przeglądarki, bez wykonywanie jakiejkolwiek komunikacji z serwerem. Jest to jednak zagadnienie na kolejny post.

środa, 14 lipca 2010

Najważniejsze cechy protokołu HTTP

Przed przystąpieniem do omawiania technik optymalizacji stron internetowych trzeba wyjaśnić na jakiej zasadzie wymieniane są dane pomiędzy użytkownikiem a serwerem. Jest to konieczne do zrozumienie gdzie mogą powstawać wąskie gardła spowalniające działanie strony i w jaki sposób można je wyeliminować. Kluczowym elementem jest tu protokół HTTP za pomocą którego dochodzi do połączenia przeglądarki z serwerem na którym znajdują się żądane przez użytkownika zasoby. Obecnie obowiązującą wersją protokołu jest HTTP 1.1, jednak używana jest również jego poprzednia wersja 1.0. Z dokładną specyfikacją techniczną można zapoznać się na stronie W3C.

Podstawową metodą protokołu HTTP jest metoda GET, ma ona również zasadnicze znaczenie dla kwestii wydajności i dlatego to właśnie ona będzie głównym przedmiotem analizy. Metoda GET służy do wysłania przez przeglądarkę żądania dokumentu o określonym adresie URI oraz odebrania tego dokumentu.

Podczas drogi od przeglądarki do serwera oprócz adresu zasobu żądanie to zawiera również nazwę hosta, informację o wersji protokołu HTTP, nazwę i wersję przeglądarki z której pochodzi żądanie, a także inne informacje. Z punktu widzenia optymalizacji najistotniejsza jest właściwość określająca czy przeglądarka użytkownika akceptuje odbieranie treści w postaci skompresowanej, a jeżeli tak to jakiego typu kompresja może zostać wykorzystana. Jeżeli użytkownik nie akceptuje kompresji to nic nie da się poradzić i należy dostarczyć mu wersję nieskompresowaną. Jednak w przypadku, gdy kompresja jest akceptowana, to bardzo dobrym rozwiązaniem zastosowanie jej. (Więcej na ten temat w następnych postach)

Z punktu widzenia optymalizacji znacznie ważniejsza jest odpowiedź jakiej udziela serwer i parametry protokołu HTTP jakie się w niej znajdują. Za ich pomocą można bowiem określić jak zachowywać się będzie przeglądarka podczas kolejnych żądań tego samego zasobu. Dzięki temu możliwe jest przyspieszenie realizacji przyszłych żądań i odciążenie serwera.

Zanim jednak przejdziemy do omówienia konfiguracji nagłówków protokołu HTML konieczne jest przyjrzenie się w jaki sposób przeglądarka ustala czy powinna pobrać dany zasób z sieci, czy nie. Przeglądarki i systemy operacyjne posiadają tak zwany bufor, w którym przechowują informacje, z których użytkownik już kiedyś korzystał. Co za tym idzie konieczność pobrania danego zasobu z sieci zachodzi tylko wtedy, gdy nie ma go w buforze, lub gdy wersja w buforze jest nieaktualna.

Podstawowym mechanizmem regulującym tę kwestię jest warunkowe żądanie GET, polegające na wysłaniu żądania zawierającego parametr ‘If-Modified-Since’. Jeżeli żądany zasób był modyfikowany po dacie wskazywanej przez ten parametr, to zostanie on ponownie pobrany, tak aby użytkownik korzystał z aktualnej wersji. Jeżeli jednak nie był on modyfikowany to serwer zwróci odpowiedź zawierającą jedynie kod stanu ‘304 Not Modified’, a przeglądarka skorzysta z wersji przechowywanej w buforze. Dzięki temu skrócony zostaje czas oczekiwania, gdyż nie trzeba przesyłać zasobów przez sieć, lecz wystarczy pobrać je z dysku twardego. Ponadto odciążony zostaje serwer, który dzięki temu jest w stanie efektywniej przetwarzać te żądania, które muszą być przez niego obsłużone.

Rozwiązanie to, mimo że znacznie przyspiesza ponowne ładowanie strony, nie jest jeszcze maksimum wydajności jakie da się osiągnąć. Co prawda zasoby nie są przesyłane przez sieć, ale nadal konieczne jest wysłanie żądania i pobranie odpowiedzi potwierdzającej możliwość pobrania zasobu z bufora. Do rozwiązania tej kwestii służą parametry Expiers i Keep-Alive. Definiują one jak długo dany zasób pozostaje aktualny, tak żeby przeglądarka była w stanie podjąć decyzję o jego pobieraniu bądź nie bez konieczności komunikowania się z serwerem. Okres ważności danego zasobu jest wtedy przechowywany razem z nim w buforze i może być bezpośrednio odczytywany przez przeglądarkę. Dzięki temu nie ma konieczności przesyłania po sieci jakichkolwiek informacji.

Korzystanie z parametrów zawartych w nagłówkach protokołu HTTP i minimalizacja ilości żądań i przesyłanej treści jest zagadnieniem znacznie obszerniejszym. Więcej na ten temat w kolejnych postach.

poniedziałek, 28 czerwca 2010

Co to znaczy „wystarczająco szybko”?

Oczywiście dobrze jest gdy aplikacja działa tak szybko, że użytkownik podczas pracy w ogóle nie zauważa opóźnień w działaniu interfejsu i nie musi „czekać” na program. Stan ten jest jednak trudny do osiągnięcia nawet w aplikacjach typu desktop, nie mówiąc już o aplikacjach internetowych. Dlatego dobrze jest określić co to znaczy „wystarczająco szybko” i dążyć do tego, aby tworzone aplikacje zbliżały się do tego punktu.

To co użytkownik uzna za akceptowalne tempo pracy danej aplikacji jest zjawiskiem psychologicznym nie da się go więc precyzyjnie wyznaczyć. Co innego będzie akceptowane przez różnych użytkowników, co innego również będzie akceptowalne dla tego samego użytkownika w zależności od wagi jaką przykłada do danego zagadnienia. Dodatkowym czynnikiem jest subiektywne odczucie użytkownika o tym jak skomplikowanych operacji wymaga żądane przez niego działanie. Jeżeli uważa on, że jest to coś trudnego i maszyna musi się przy tym „napracować” to jest on w stanie wybaczyć jej znacznie dłuższy czas oczekiwania. Co za tym idzie określając czas jaki będzie akceptowalny dla potencjalnych użytkowników należy brać pod uwagę rodzaj informacji jakich dostarczać będzie witryna oraz jak dużo przeciętny użytkownik będzie skłonny na nie czekać.

Poniżej zamieszczamy ogólne wytyczne co do subiektywnego odbioru czasu jaki poświęcić trzeba oczekując na wyświetlenie strony.
- 0,1 sekundy: Opóźnienie takie daje użytkownikowi wrażenie, że bezpośrednio oddziałuje na dane obiekty. Jest to czas, którego nie traktuje on jako oczekiwanie na wykonanie zadanego polecenie. W przypadku aplikacji internetowych jest to poziom praktycznie nieosiągalny i może być traktowany głównie jako pewien punkt odniesienia.
- do 1 sekundy: Podobnie jak w poprzednim przypadku użytkownik odnosi wrażenie płynności interfejsu, chociaż czas potrzeby na odpowiedź aplikacji jest już zauważalny. Poziom ten można osiągnąć gdy nie zachodzi potrzeba ponownego wczytania całej strony, jest to więc poziom oczekiwany dla zadań opartych o technologię AJAX.
- kilka sekund: Użytkownik odnosi wrażenie, że komputer „pracuje nad powierzonym mu zadaniem”. W przypadku aplikacji typu desktop, jeżeli zadanie nie jest uznawane za trudne i wymagające czasu, użytkownik odniesie już wrażenie, że aplikacja działa ospale i nie będzie z niej zadowolony. W przypadku witryn internetowych jest to poziom akceptowalny przy wczytywaniu całych stron, jednak pod warunkiem, że widać postępy w realizacji zadania. Jeżeli strona nie odpowiada przez kilka sekund i wyświetla jedynie biały ekran, to użytkownik najprawdopodobniej rozważa już jej opuszczenie, a z pewnością pierwsze wrażenie nie jest pozytywne.
- granica 10 sekund: Jest to orientacyjna bariera, po przekroczeniu której użytkownik traci cierpliwość i nie ma już ochoty dłużej oczekiwać na realizację swojego żądania. Poziom ten nie powinien występować w normalnych przypadkach związanych z wyświetlaniem stron internetowych. Użytkownik może go zaakceptować jedynie wtedy, gdy wie, że na żądanej stronie będzie interesująca go informacja, lub gdy realizacja żądania będzie gwarantować mu osiągnięcie celu. Co za tym idzie musi to być użytkownik już związany ze stroną, który zna jej sposób funkcjonowania i rezultaty poszczególnych żądań. Dla nowych użytkowników będzie to sygnał do natychmiastowego opuszczenia witryny. Jeżeli jakieś zadanie z pewnych względów wymaga tak długiego czasu oczekiwania, a potencjalni użytkownicy będą w stanie je tolerować, to należy im to wyraźnie zasygnalizować i informować na bieżąco o postępach w jego realizacji. Pomocne mogą tu być różnego rodzaju paski postępu.

Wnioski:
1) Proste zdarzenia nie wiążące się z ponownym wczytaniem strony lub przejściem na inną stronę (np.: różne zastosowania technologii AJAX) powinny trwać krócej niż sekundę.
2) Wczytywanie strony może trwać kilka sekund, lecz absolutnie nie więcej niż dziesięć. Poziomem, do którego odczucia użytkownika są pozytywne, jest około pięć sekund. Użytkownik powinien na bieżąco widzieć postępy w wykonywaniu prac (progresywne ładowanie strony).
3) Zadania wymagające więcej niż dziesięciu sekund zawsze powinny być sygnalizowane użytkownikowi, a stopień ich realizacji powinien być na bieżąco wyświetlany na pasku postępu, tak aby łatwe było oszacowanie czasu jako pozostał do ukończenia zadania.

wtorek, 15 czerwca 2010

Optymalizacja - od czego zacząć?

Czy należy optymalizować serwisy internetowe? Jeżeli chcemy, aby prezentowana przez nas treść miała chociaż potencjalną szansę dotrzeć do użytkownika to odpowiedź jest prosta – TAK! W innym przypadku użytkownik znudzony przeciągającym się oczekiwaniem na wyświetlenie treści z pewnością opuści naszą stronę i trefi do konkurencji, która była w stanie stworzyć serwis działający wystarczająco szybko. To ostatnie określenie jest oczywiście problematyczne, ale zajmiemy się nim następnym razem starając się ustalić "co to znaczy wystarczająco szybko?". Teraz natomiast rozpatrzmy kwestię od czego należy rozpocząć planowanie przyspieszania naszej aplikacji internetowej.

Wyciśnięcie maksymalnej wydajności z każdego elementu jest stanem równie pożądanym co niewykonalnym. Proces taki wymagałby zazwyczaj prześledzenia każdego nawet najmniejszego elementu, zbadania jego wpływu na pozostałe elementy, kolejnych faz optymalizacji i pomiarów. W efekcie konieczne byłoby gruntowne przebudowanie całego sytemu, często wiążące się również ze zmianami sprzętowymi. Proces taki byłby niezwykle drogi i czasochłonny a osiągnięty efekt mógłby nie być wart takich poświęceń. Dlatego warto zoptymalizować te czynniki, które mają największy wpływ na czas jaki użytkownik musi poświęcić na otrzymanie żądanej treści. Jeżeli uda nam się przyspieszyć o 30% elementy odpowiedzialne za 80% czasu wyświetlania strony, to osiągniemy więcej niż przez zredukowanie do zera czasu przeznaczanego na pozostałe 20%. Dlatego skupić należy się na tych elementach, które są najbardziej czasochłonne, a które można znacznie przyspieszyć bez całkowitej przebudowy systemu i ponoszenie znacznych kosztów.

W przypadku wielu stron internetowych podstawowym obszarami zainteresowania powinny być następujące kwestie:
- zmniejszenie ogólnego rozmiaru strony, w szczególności plików graficznych oraz stylów CSS i kodu JavaScript
- zmniejszenie ogólnej liczny żądań HTTP
- wykorzystanie buforowania elementów przez przeglądarki

Cele te da się z dobrym rezultatem osiągnąć poprzez optymalizacje plików graficznych, stosowanie CSS Sprite, kompresję treści przesyłanej do przeglądarki, zarządzanie nagłówkami Expires, odpowiednie rozmieszczenie elementów w dokumencie HTML oraz optymalizację struktury plików CSS i JavaScript. Co więcej zabiegi te pozwalają w większości przypadków nawet dwukrotnie przyspieszyć czynniki odpowiedzialne za 60 – 80% czasu oczekiwania na wyświetlenie strony.

Dodatkową zaletą jest również obniżenie kosztów funkcjonowania strony poprzez zmniejszenie ruchu sieciowego przy nie zmienionej liczbie wyświetleń strony.