31 października 2009, 00:33 dario Komentarze (0)

MSCD - jak to działa

Mini Content Static Delivery, czyli Mscd powstał z potrzeby uruchomienia projektu ASP.NET MVC na Windows Server 2003 i IIS6. Jak wiadomo uruchomienie takiej aplikacji na IIS6 niesie za sobą kilka niedogodności związanych z obsługą dostarczania kontentu statycznego. Wiedziałem, że serwis musi działać optymalnie, więc zacząłem drążyć temat. Pierwszy link na który sie natknąłem (post Omara Al Zabira) pokazywał rozwiązanie jak sprytnie serwować kontent statyczny w sposób optymalny. Uruchomienie aplikacji asp.net mvc na IIS6 z ładnymi adresami url można wykonać na kilka sposobów, ale mi najbardziej spodował się właśnie patent z włączeniem "wildcard mapping". Niestety sposób ten wymusza przekazywanie każdego żądania do silnika aplikacji MVC, który musi je wszystkie obsługiwać. I tak zamiast zajmować się aplikacją musi dodatkowo serwować kontent statyczny. Standardowo kontent statyczny obsługiwany jest w takim przypadku przez mało wydajny DefaultStaticHandler, który m.in. nie buforuje plików w pamięci, co wiąże się z tym iż każde nowe żądanie o plik (przykładowo) typu CSS wymusza odczyt z dysku. Handler Omara jest super, ale dla mnie było jeszcze za mało. Dlatego wpadłem na pomysł całkowitego odseparowania części serwującej kontent statyczny od głównej aplikacji. Tak właśnie powstał projekt Mini Static Content Delivery. Mscd wykorzystuje w swoim działaniu funkcjonalność handlera napisanego przez Omara, a dodatkowo robi kilka rzeczy więcej. O tym w sumie już pisałem, a teraz zajmę się zasadami działania.   GZip/Deflate Wybrany kontent statyczny kompresowany jest jedną z metod, którą w przysłanym nagłówku deklaruje przeglądarka. Do wyboru mamy GZip lub Deflate. Jedna z tych metod kompresji będzie wykonana jeśli żądanie jest przesłane protokołem nie starszym niż HTTP 1.1 oraz nie jest to IE6. Tak jak wspominałem wcześniej DefaultStaticHanlder jest nieoptymalny, ale Mscd buforuje pliki, a dodatkowo operacja kompresji odbywa się tylko raz przy pierwszym odczycie z dysku. W buforze zapisywane są 3 wersje:

  • kontent źródłowy
  • kontent skompresowany metodą GZip
  • kontent skompresowany metodą Deflate

Dzięki takiemu zabiegowi oszczędzamy moce przerobowe na serwerze i jesteśmy w stanie szybko zaserwować żądaną treść.

W ustawieniach Mscd można zdecydować, którego rodzaju pliki mają być kompresowane oraz którego rodzaju pliki mają być wogóle obsługiwane przez Mscd. Pozostałe będą przeźroczyście obsługiwane przez IIS.
Optymalizacja Kolejną cechą Mscd jest optymalizacja kodu CSS oraz JS. W tym celu Mscd używa modułu YUI Compressor. Dzięki temu z kodu wycinane są wszystkie zbędne znaki takie jak spacje z początku i końca wiersza, znaki przejścia do następnej linii, komentarze oraz wykonywane jest wiele różnych zabiegów zmniejszających kod wyjściowy. Co dokładnie potrafi YUI Compressor możecie poczytać na źródłowej stronie projektu. Napisałem wcześniej, że buforowany jest m.in. kontent źródłowy. Tu musze nadmienić, że źródłowy oznacza nieskompresowany, ale jest za to zoptymalizowany. Tu także widać, że dzieje się to tylko za pierwszym odczytem danych z dysku.   Paczki O paczkach wspominałem w poprzednim poście. Tu podam przykład jak taką paczkę stworzyć. Najczęściej jest tak, że strona potrzebuje więcej niż jeden plik CSS czy JS. Same pluginy JQuery korzystają z własnych CSS'ów. Używając kilku mamy i kilka CSS'ów jak i kilka JS'ów. Standardowo w kodzie strony byśmy mieli coś takiego (przykład strony YUI).  

  Tu widać dwa odwołania, ale co by się działo, gdyby tych cssów było kilkanaście?  Używając Mscd zamiast dwóch czy więcej odwołań mielibyśmy jedno odwołanie po CSS i jedno po JS (przykład strony fotoline.pl

):  

  Dlaczego mniej odwołań do serwera znaczy lepiej nie trzeba nikomu tłumaczyć, ale stworzenie paczki z kilku lub nawet kilkunastu pluginów + samo jquery + własne pliki skryptów js z konkretną funckcjonalnością przynosi naprawdę wymierne efekty. W jednej z moich aplikacji biznesowych potrzebowałem sporej funkcjonalności po stronie przeglądarki. Uzbierało się 432kB skrytpów. Szok, ale skompresowana i zoptymalizwoana paczka waży jedynie 85kB(!!!). Dodatkowo wysłany kontent posiada ustawiony nagłówek powodujący buforowanie tego kontentu na rok czasu. Co oznacza, że przy kolejnych wejściach na stronę  przeglądarka będzie pobierać te skrypty już ze swojego lokalnego bufora. :)   Wersjonowanie Wersjonowanie niestety nie jest jeszcze optymalne. Tu się przyznaję, że sprawdzanie ostatniej daty zmiany pliku na dysku nie jest super wydajne, ale... zaraz, zaraz. Doskonale się sprawdziło w projekcie o którym wspominałem. Serwer wogóle tego nie odczuwa. Między innymi także dlatego, że kontent jest agresywnie bufowowany po stronie przeglądarki, więc tak naprawdę tylko pierwsze pobranie kosztuje sprawdzenie ostatniej daty zmiany pliku. W każdym bądź razie mam pomysł jak rozwiązać ten problem, ale o tym inną razą. Wracając do wersjonowania, działa to tak, że aplikacja zanim wygeneruje link w kodzie strony sprawdza tą datę i generuje z niej hash (int), który zapisywany jest w postaci szesnastkowej. Stąd na powyższym przykładzie nazwa pliku to Site_Default_vee5ac2a5.css. Oczywiście jest to wersja obejmująca datę ostatniej modyfikacji plików z katalogu paczki. Po co wersjonowanie? Agresywne buforowanie (jak pisałem na rok do przodu) powoduje, że nowy kontent (poprawka w skrypcie) nie został by wysłany do przeglądarki, bo przeglądarka o niego nie będzie pytać. Aby uniknąć tego problemu każda modyfikacja treści powoduje przeładowanie kontentu w buforze po stronie serwera, ponowną optymalizację i kompresję oraz zapisanie do bufora nowej wersji. Modyfikacja oznacza, że wygenerowany hash wersji będzie inny, a to przekłada się na zmianę nazwy pliku. Przeglądarka w takim wypadku wyśle żądanie do serwera po nowy kontent.   Buforowanie po stronie przeglądarki Tak jak pisałem powyżej po pobraniu nowej treści przeglądarka ponownie zapisze go w swoim buforze na najbliższy rok. Rok to standardowe ustawienie Mscd, ale można je zmodyfikować w ustawieniach. A co z HTTPS? Przeglądarka zapisuje w buforze kontentu jeśli pochodzi z zabezpieczonego kanału. Wchodząc kolejny raz na tą samą stronę przeglądarka pobiera kontent od nowa. Czyli co? Mogło by się wydawać, że utracimy naszą wydajność. Na szczęście nie do końca. Kontent jest buforowany przez przeglądarkę, ale tylko w ramach danej sesji, czyli jednostajnej wizyty na danej stronie. W takim przypadku przeglądarka za każdym razem będzie pytać o nowy kontent, ale Mscd wysyłając treść wysyła także nagłowek "If-Modified-Since", który przy żądaniach jest odsyłany przez przeglądarkę spowrotem do serwera. Nagłówek ten jako wartość zawiera datę ostatniej modyfikacji. Mscd sprawdza czy data ta jest inna niż data ostatniej modyfikacji pliku/paczki na dysku i w przypadku, gdy są takie same wysyła tylko informację o tym iż treść się nie zmieniła od ostatniego pobrania. Dokładniej rzecz biorąc odsyła nagłówek "Not-Modified", czyli status 304 i zerową ilość treści. Pamiętając iż poprzez paczki zmniejszamy ilość wywołań do minimum aplikacja i tak działa optymalnie oszczędzając zasoby serwera jak i łącza. Oczywiście po zamknięciu przeglądarki zbuforowany kontent zostanie usunięty i  przy kolejnym wejściu cały kontent ponownie musi być dostarczony to i tak podczas pracy z aplikacją użytkownik będzie widział poprawę szybkości działania.   Aplikacja osobno a Mscd osobno Rozdzielenie na dwie odrębne aplikacje ma dodatkowe znaczenie. Po pierwsze w realnym świecie aplikacje dostępne są zazwyczaj pod jakąś domeną. Podział aplikacji głównej i aplikacji do serwowania kontentu statycznego na dwie różne domeny umożliwia przeglądarce na pobieranie treści z dwóch różnych źródeł naraz. Zdaje się, że wszystkie współczesne przeglądarki mają takie ograniczenie, że z jednej domeny może być jednocześnie wykonywane tylko jedno żądanie, a maksymalnie dwa żądania równoległe z dwóch różnych domen. Po drugie aplikacje internetowe dosyć często wykorzystują ciasteczka. Czasami są one pokaźnych rozmiarów. Kiedy kontent statyczny (cssy, jsy, obrazki) byłby ładowany z tej samej domeny co html wygenerowany przez aplikację główną to przeglądarka przy każdym żądaniu do tej domeny wysyłała by nasze ciasteczka. Nawet, gdy jest to żądanie po malutki obrazek symbolizujący kierunek sortowania. W przypadku gdy kontent serwowany jest z innej domeny przeglądarka nie wysyła ciasteczek. Znów mamy oszczędność zasobów łącza.   Obrazki i inne pliki Obrazki oczywiście nie są kompresowane ani optymalizowane. Choć tu można by się pokusić o jakiś pomysł. W końcu istnieją serwisy do optymalizacji obrazków, na przykład smush.it. Ale to jest pieśń przyszłości i narazie o tym wogóle nie myślę. Dodatkowo każde odwołanie z treści CSS o obrazek jest odpowiednio parsowane i takiemu obrazkowi generowany jest hash, który jak już wiecie stanowi część nazwy pliku. CSS jest parsowany - Mscd wyszukuje odwołania po Jpegi czy PNGi i zmienia nazwy po to, aby pliki te także mogy być buforowane. Zerknijcie w kod CSS mojej stronki firmowej: http://static.softio.pl/styles/main_vbdc791b.css   Podsumowując Mscd pomaga zoptymalizować ruch między przeglądarką a serwerem zachowując jednocześnie elastyczność przy modyfikacjach kontentu statycznego. Bardzo łatwo jest poprawić buga w jakimś skrypcie i nie martwić się o to w jaki sposób nowa treść zostanie dostarczona do przeglądarki. Poprostu podmieniamy treść, dodajemy czy usuwamy i już. Reszta zadzieje się automagicznie. Nie wiem czy czegoś nie pominąłem. Na pewno brakuje opisu ustawień oraz tego jak to draństwo skonfigurować, aby zadziałało na IIS6 czy 7 oraz opisu API w jaki sposób uzyskać odpowiedni link po kontent statyczny z aplikacji Mscd. O tym wszystkim będzie kolejny post, więc bądźcie w pobliżu. :)

Tagi:

Projekty

Komentarze zablokowane

O autorze

Dariusz Gil - projektant i programista aplikacji internetowych budowanych na platformie Microsoft w technologii ASP.NET (C#) oraz MS SQL Server. Obecnie właściciel (narazie :)) jednoosobowej firmy Softio.

Filtruj używając APML