• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
 

Optymalizacja aplikacji ASP.NET

on

  • 1,681 views

Prezentacja opisuje różne techniki optymalizacji aplikacji ASP.NET. Omawiane są role poszczególnych warstw wpływających na wydajność - od optymalizacji kodu po stronie klienta (techniki ...

Prezentacja opisuje różne techniki optymalizacji aplikacji ASP.NET. Omawiane są role poszczególnych warstw wpływających na wydajność - od optymalizacji kodu po stronie klienta (techniki stosowane na poziomie kodu HTML i JavaScript) przez różne poziomy stosowania cache, wybrane ustawienia konfiguracyjne IIS aż po same techniki optymalizacji na poziomie kodu ASP.NET.

Statistics

Views

Total Views
1,681
Views on SlideShare
1,673
Embed Views
8

Actions

Likes
0
Downloads
2
Comments
0

2 Embeds 8

http://www.linkedin.com 5
https://www.linkedin.com 3

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Optymalizacja aplikacji ASP.NET Optymalizacja aplikacji ASP.NET Presentation Transcript

    • Optymalizacja aplikacji ASP.NET
    •  Skalowalność – ilu użytkowników może pracować jednocześnie?  Aplikacja może obsługiwać tysiące użytkowników, ale być wolna  Zmiany w sprzęcie, infrastrukturze sieciowej, itp. (istotna architektura!)  Wydajność – jak szybko ładują się strony?  Zasada 8 sekund  Liczy się odczuwalna wydajność (np. reklamy dopiero na końcu)  To także:  Szybkość wprowadzania poprawek i zmian  Łatwość wdrażania aplikacji  Proces wytwórczy  …  Architektura Bing vs strony domowej (można przesadzić) Skalowalność i wydajność
    •  Lokalny cache przeglądarki  DNS – cache / zapytanie  Proxy – visible / transparent  Cache serwera  System - http.sys  IIS  ASP.NET (dane w ram / z dysku)  Cache SQL Server  Dyski – SSD / zwykłe / cache… Komponenty wpływające na wydajność
    •  Odczuwalna wydajność  Kolejność ładowania, AJAX  Mniej żądań do serwera  Walidacja po stronie klienta, bundling, itp.  Cache na wszystkich warstwach  Minimalizacja blokujących wywołań  Synchroniczne operacje zapisu do bazy, zewnętrzne usługi, itp.  Optymalizacja I/O (dyski, itp.)  Także partycjonowanie / sharding, itp. Najważniejsze zasady przy optymalizacji
    • Wydajność po stronie klienta
    •  Maximum Transfer Unit (MTU) - rozmiar okna odpowiedzi  Od 500 do 1500 bajtów  Dużo komunikatów SYN-ACK  TCP Slow Start (RFC 5681)  Bardzo kosztowne nawiązywanie połączenia  Długi okres między pierwszym pakietem a kolejnym  Zapobiega przeciążeniu sieci  Jak najmniej nowych połączeń (np. żądania plików)  HTTP Keep-Alives – domyślnie w IIS 120 sekund  Np. przy długich formularzach można zwiększyć (ostrożnie) Protokół TCP
    • TCP Slow Start - przykład  IE Developer tools (F12)  Żółte – slow start + pierwszy pakiet odpowiedzi  Niebieski – reszta odpowiedzi  Jeśli <img> było w pierwszym pakiecie
    •  <head>  Przeglądarka nie wyświetli nic przed pobraniem całego nagłówka  „Lookahead” – ograniczenia  Jak najwięcej w <body>  Nawet <link> i <style> (niezgodne ze specyfikacją, ale działa)  Kolejność  Np. duży baner reklamowy na górze strony – w kodzie lepiej niżej Struktura strony
    •  Ustawiać width i height dla <img>! Późne ładowanie - placeholder <img id="myimg" width="50" height="50" /> <!-- Klasycznie --> <script type="text/javascript"> document.getElementById("myimg").src = "myimage.jpg"; </script> <img id="Img1" width="50" height="50" /> <!-- w jQuery --> <script type="text/javascript"> $("#myimg").attr("src", "myimage.jpg"); </script>
    • Późne ładowanie – preloading <!-- Preloading - na górze strony lub w onload (jeśli może być później) --> <script type="text/javascript"> var myimg = new Image(); myimg.src = "myimage.jpg"; </script> <!-- Niżej na stronie, będzie załadowane z cache (dla rolloverów) --> <img src="myimage.jpg" width="50" height="50" />
    •  Wielkość liter  W Unix – system plików case sensitive  Niektóre serwery cache’ują oddzielnie  Może spowodować wysłanie dwóch żądań  Można dołączyć moduł http, który to poprawia  Referencje do tej samej domeny  Przekierowanie z domena.com/gfx.jpg na www.domena.com/gfx.jpg Cache i URL <img src="myimage.jpg" width="50" height="50" /> <img src="myimage.JPG" width="50" height="50" />
    •  Przeglądarki mają do 6 równoległych połączeń dla domeny  Przed IE8 – tylko 2!  Można podzielić pliki na kilka domen  img.mojadomena.com, css.mojadomena.com, itp.  Aliasy lub inne usługi cookieless (np. Azure Blob) Przetwarzanie żądań <img src="q1.gif" height="16" width="16" /> <img src="q2.gif" height="16" width="16" /> <img src="q3.gif" height="16" width="16" /> <img src="q4.gif" height="16" width="16" /> <img src="q5.gif" height="16" width="16" /> <img src="q6.gif" height="16" width="16" /> <img src="q7.gif" height="16" width="16" /> <img src="q8.gif" height="16" width="16" /> <img src="q9.gif" height="16" width="16" /> <img src="q10.gif" height="16" width="16" />
    •  ok. 30% zwiększenie szybkości wczytywania Podział plików na kilka domen <img src="q1.gif" height="16" width="16" /> <img src="q2.gif" height="16" width="16" /> <img src="q3.gif" height="16" width="16" /> <img src="q4.gif" height="16" width="16" /> <img src="q5.gif" height="16" width="16" /> <img src="http://mojadomena.net/samples/ch02/q6.gif" height="16" width="16" /> <img src="http://mojadomena.net/samples/ch02/q7.gif" height="16" width="16" /> <img src="http://mojadomena.net/samples/ch02/q8.gif" height="16" width="16" /> <img src="http://mojadomena.net/samples/ch02/q9.gif" height="16" width="16" /> <img src="http://mojadomena.net/samples/ch02/q10.gif" height="16" width="16" />
    •  Jeśli kilka aliasów – mechanizm generowania powtarzalnych url  Plik grafika.jpg zawsze z s1.domena.com, grafika3.jpg z s2.domena.com, itp.  Wewnątrz własnej kontrolki  Control adapter dla Image (dalej) ASP.NET i podział na kilka domen private string _src; private static string[] subdomains = { "http://s1.12titans.net", "http://s2.12titans.net", "http://s3.12titans.net" }; public string src { get { HttpContext ctx = HttpContext.Current; if (ctx.Request.Url.Host != "localhost") { if (!String.IsNullOrEmpty(this._src) && !this._src.StartsWith("http") && !this._src.StartsWith("data:")) { int n = Math.Abs(this._src.GetHashCode()) % subdomains.Length; return subdomains[n] + this._src; } } return this._src; } set { this._src = ResolveUrl(value).ToLowerInvariant(); } }
    •  Skrypty inline mogą opóźniać renderowanie strony  Renderowanie dopiero po zakończeniu działania skryptów  OnLoad / DOMReady – wtedy po wyrenderowaniu  Umieszczać na końcu pliku  Jeśli skrypty zmieniają HTML  Zamiast document.write() – innerHTML (możliwe wywołania później)  Ukryty div z document.write() + odkrywanie go później  <script defer> i <script async> (HTML 5)  Nie wstrzymuje parsera, pobiera skrypt i wykonuje kod (np. podpina do onload)  Async – nie gwarantuje kolejności (wywołuje po pobraniu)  Defer – gwarantuje kolejność wywołań  Dołączać z CDN (m.in. ASP.NET AJAX, jQuery) - http://www.asp.net/ajaxlibrary/cdn.ashx Skrypty
    •  Tylko lower case w miarę możliwości – kompresja  <img> zamiast <IMG>, itp.  Image sprites dla wielu mniejszych grafik  background-position: 0 -120px  Grafika - rozważyć data URI scheme (IE 8+)  Narzędzia lub online – np. dataurl.net  Do niewielkich grafik (base64, więc 40% większe)  Zwłaszcza w CSS, kiedy może być dodatkowo cache’owane Zmniejszanie liczby żądań #hdr{border:1px solid #000;height:40px; background:url(data:image/gif;base64,R0lGODlhAQAoANUAAAAAAP///wFUzgNV0ANVzwVX0QZY0gdY0gha 0wpb1Qxd1g1e1w9g2BFh2RJj2xVk3BZm3Rho3hpp4Bxr4h5t4x9u5CFw5SRx5yVz6Cd16Sl26it47C157S987jF97 zOA8jJ+8TWB8zaD9DiE9TqF9zqG9zyH+D2I+D6J+T+K+v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACoALAAAAAABACgAAAYlwBTqZCqRRqLQB+TpcDa aDOZiqVAmkgjk4WgwFooE4mAoDAiCIAA7)
    •  Javascript  Walidacja po stronie klienta  Wyłączanie submit po kliknięciu  Generowanie długich list (np. select i wiele elementów <option>)  Unikać obiekt.inny.jeszczeinny.zmienna (pomocnicza zmienna)  Wielokrotny document.write zamiast sklejania stringów  textContent szybszy od innerHTML (jeśli element zawiera tekst)  ASP.NET 4.5 – unobtrusive validation Zmniejszenie liczby żądań – c.d. <code><add key="ValidationSettings:UnobtrusiveValidationMode" value="WebForms"/></code> ValidationSettings.UnobtrusiveValidationMode = UnobtrusiveValidationMode.WebForms;
    •  Favicon.ico – lepiej żeby był (cache vs 404 za każdym razem)  CSS – inline przy pierwszym wyświetleniu  Mniej żądań przy pierwszym wejściu  Skrypt ładowany inline – jeśli nie ustawione ciasteczko  W Onload – pobierany dynamicznie (cache dla kolejnych żądań)  Nie zamiast – plik potrzebny (cache) Zmniejszenie liczby żądań – c.d. <script type="text/javascript"> function getcss() { var h = document.getElementsByTagName('head'); var l = document.createElement('link'); l.type = 'text/css'; l.rel = 'stylesheet'; l.href = 'css/file19.css'; h[0].appendChild(l); } </script> <system.webServer> <httpProtocol> <customHeaders> <add name="Set-Cookie" value="C=A;expires=Sat, 01-Jan-2050 00:00:00 GMT;path=/demo/css/" /> </customHeaders> </httpProtocol> </system.webServer>
    •  Określić <!DOCTYPE>  Parser lookahead nie musi restartować  Width i Height przy grafikach  Rozmiary kolumn – tabele  Charset (dla statycznych stron) Szybkość renderowania strony
    •  Kiedy wiemy jaka będzie kolejna strona (np. „wizard”)  W pageLoad (nie document ready) Precaching - grafiki <script type="text/javascript" src="jquery-1.7.1.min.js"></script> <script type="text/javascript"> $(window).load(function() { var pre = new Image(0,0); pre.src = "http://s1.domena.net/static/next1.jpg"; var prx = new Image(0, 0); prx.src = "http://s2.domena.net/static/next2.jpg"; }); </script>
    •  <img>- dla innych formatów nie zadziała (MIME type)  AJAX – można, ale tylko ta sama domena  Dynamicznie element <script>  Nie musi być dodawany do DOM  Uwaga – parsowane i wywoływane  Dynamicznie <link>  Musi być w DOM  Wczytywany (może być konflikt selektorów) Precaching – CSS, JS <script type="text/javascript"> function preload() { var req = getreq(); if (req != null) { req.open("GET", "/static/next.js", true); req.send(null); } var rex = getreq(); if (rex != null) { rex.open("GET", "/static/next.css", true); rex.send(null); } } </script> <script type="text/javascript"> function preload() { var scr = document.createElement("script"); scr.src = "http://s1.domena.net/ch02/next.js"; } </script> <script type="text/javascript"> function preload() { var lnk = document.createElement("link"); lnk.rel = "stylesheet"; lnk.type = "text/css"; lnk.href = "http://s1.domena.net/next.css"; document.getElementsByTagName('head')[0].appendChild(lnk); } </script>
    • Caching
    •  Każdy ma nieco inną rolę (np. ASP.NET vs http.sys vs SQL) Wiele rodzajów cache
    •  Cache-Control: max-age lub Expires (dawniej)  Jeśli nie ustawione oblicza  Aktualny czas +10% różnicy między Last-Modified a aktualnym  Po tym czasie – nadal na dysku, ale nie używane  Conditional Get (If-Modified-Since)  HTTP 304 – not modified Statyczne pliki GET /check.png HTTP/1.1 Accept: */* Accept-Language: en-us Accept-Encoding: gzip, deflate If-Modified-Since: Sat, 10 Jan 2012 10:52:45 GMT If-None-Match: "80fc52fa8bb2c81:0" User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0) Host: www.domena.net Connection: Keep-Alive <system.webServer> . . . <staticContent> <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00" /> </staticContent> </system.webServer>
    •  Cache-Control: no-cache  Przeglądarka musi sprawdzić, ale może korzystać z cache (back/fwd)  Cache-Control: no-store  Całkowite wyłączenie (także back/forward) Cache statyczny - wyłączanie <location path="image.jpg"> <system.webServer> <staticContent> <clientCache cacheControlMode="DisableCache" /> </staticContent> </system.webServer>
    •  Output cache  VaryByParam, VaryByHeader – np. Accept-Language (dla różnych języków)  VaryByControl (np. „src”, jeśli taką właściwość ma nasza User Control)  Dla User Controls – ustawiać to samo ID na różnych stronach i Shared=true  Jeśli shared=false to do porównywania ID brana także nazwa Page Dynamiczna zawartość <%@ Page Language="C#" AutoEventWireup="true" CodeFile="dyn-client.aspx.cs" Inherits="dyn_client" %> <%@ OutputCache Duration="86400" Location="Client" VaryByParam="None" %> <system.web> <caching> <outputCacheSettings> <outputCacheProfiles> <add name="Cache1Day" duration="86400" location="Client" varyByParam="none" /> </outputCacheProfiles> </outputCacheSettings> </caching> </system.web> <%@ OutputCache CacheProfile="Cache1Day" %>
    •  Najprościej – VaryByCustom = „browser”  Po swojemu - global.asax  Jeśli chcemy np. jedną wersję dla wszystkich IE i jedną dla Webkit Cache dla różnych przeglądarek public override string GetVaryByCustomString(HttpContext context, string custom) { switch (custom.ToLower()) { case "iemozilla": switch (context.Request.Browser.Browser.ToLower()) { case "ie": case "blazer 3.0": return "ie"; case "mozilla": case "firebird": case "firefox": case "applemac-safari": return "mozilla"; default: return "default"; } default: return base.GetVaryByCustomString(context, custom); } }
    • Dynamiczna zawartość - wyłączanie this.Response.Cache.SetCacheability(HttpCacheability.NoCache); this.Response.Cache.SetAllowResponseInBrowserHistory(true); // Aby nie było Expires (niepotrzebne) this.Response.AppendHeader("Cache-Control", "no-store"); // Dla no-store <%@ OutputCache Location=„None" %> Cache-Control: no-cache Pragma: no-cache Expires: -1  Czasem chcemy mieć pewność, że dane aktualne  Aspx, kod lub profil w web.config
    •  Domyślnie ASP.NET ustawia Cache-Control: private  Nie będzie cache’owane przez proxy  Możliwe nadpisanie (Cache-Control: public)  <%@ OutputCache Location=„Any” %>  Location Downstream – tylko w przeglądarkach Dynamiczna zawartość – c.d. this.Response.Cache.SetMaxAge(age); this.Response.Cache.SetExpires(DateTime.UtcNow + age); this.Response.Cache.SetLastModified(DateTime.UtcNow); this.Response.Cache.SetCacheability(HttpCacheability.Public); this.Response.Cache.SetNoServerCaching();
    • Rozszerzalność <caching> <outputCache defaultProvider="AspNetInternalProvider"> <providers> <add name="DiskCache" type="Test.OutputCacheEx.DiskOutputCacheProvider, DiskCacheProvider"/> </providers> </outputCache> </caching>  ASP.NET 4.0+ – możliwe np. zapisywanie na dysku  Możliwa dynamiczna podmiana  Np. top 10 w pamięci, reszta na dysku
    •  Stan tymczasowy strony, stan kontrolek serwerowych  Często lepsze od sesji (baza przy farmie)  LosFormatter – szybki dla typów string, hashtable, arraylist, pair, triple, int, boolean  Dla pozostałych – BinaryFormatter (wolny), ale można napisać własny TypeConverter  Lepiej – kolekcja prostych typów niż własny obiekt (bez TypeConvertera)  Obecność elementu wykorzystywana m.in. do rozpoznania Page.IsPostback  ViewStateMac – zabezpieczenie przed modyfikacjami  Zabezpieczenie przez CSRF  W Page_init – ViewState[userkey] = this.User.Identy.Name; ViewState
    •  Ma tendencje do rozrastania się  Może być prościej wysłać zapytanie niż uploadować dużo większy formularz  Możliwość wyłączenia  Od ASP.NET 4 – nie tylko dla całej strony (EnableViewState), ale pojedynczych kontrolek  ViewStateMode – enabled, disabled, inherit (domyślnie)  Warto domyślnie wyłączyć (w web.config) i włączać tam, gdzie potrzeba  ControlState – część, której nie można wyłączyć ViewState - problemy
    •  Tag Transform, aby wyłączyć Control State  Jeśli nie korzystamy z zaawansowanych funkcji kontrolki ViewState – wyłączanie public class ListViewNoCS : ListView { protected override object SaveControlState() { return null; } } <pages> . . . <tagMapping> <add tagType="System.Web.UI.WebControls.ListView" mappedTagType="Samples.ListViewNoCS" /> </tagMapping> </pages>
    •  Dla wolnych połączeń (np. klienci mobile) można przechowywać po stronie serwera  Można uzależnić to od typu połączenia lub rozmiaru ViewState ViewState – wolne połączenia protected override void SavePageStateToPersistenceMedium(object state) { string key = Guid.NewGuid().ToString(); this.ClientScript.RegisterHiddenField(ViewKeyName, key); this.Cache[key] = state; } protected override object LoadPageStateFromPersistenceMedium() { string key = this.Request[ViewKeyName]; if (key == null) throw new InvalidOperationException("Invalid ViewState Key"); object state = this.Cache[key]; if (state == null) throw new InvalidOperationException("ViewState too old"); return state; }
    •  Dla domeny - max 50, 10 KB / cookie  Session lub persistent  Domyślna wartość path - „/”  Dołączane także do zawartości statycznej wszystkich plików!  Ustawiać zawsze cookie.Path = „/katalog/”  Bez ustawienia Domain  IE – także wszystkie subdomeny  Inne przeglądarki – tylko aktualna domena  Lepiej ustawić cookie.Domain (unikanie błędów)  Cookie.HttpOnly = true  Cookie niewidoczne dla JS (mniej możliwości ataku)  Powinno się ustawiać domyślnie dla wszystkich  Cookie.Secure = true – wysyłane tylko przez SSL  Jeśli nie możemy SSL – konwersja do Base64 (Convert.ToBase64String) i szyfrowanie symetryczne Cookies
    •  IE8+, Firefox 3.5+, Safari 4+, Chrome 4+, Opera 10.5+  10 MB – IE, 5 MB - pozostałe  2 typy  Per-session (do zamkniecia okna)  Per-domain  Tylko string, ale można serializować do JSON  Tylko klient – nie przesyłane do serwera  Można dodać np. jako parametr wywołania usługi WCF HTML 5 Web Storage <script type="text/javascript"> sessionStorage.setItem('mykey', 'this is my value'); var item = sessionStorage.getItem('mykey') </script>
    •  Kernel-mode driver - http.sys  Znacznie szybszy (bez context switching, itd.)  Domyślnie włączony dla zawartości statycznej  Dla dynamicznej – OutputCache, ustawienie IIS lub applicationHost.config  Kiedy nie działa (najczęstsze)  Zawartość z querystring  Domyślny dokument (d.com zamiast d.com/default.htm)  Włączona kompresja  Domyślnie sliding, 120 sekund  HKLMSystemCurrentControlSetServicesHttpParametersUriScavengerPeriod  Warto zmienić w razie potrzeby (np. kilkanaście godzin) - ostrożnie Windows Kernel Cache
    •  Domyślnie włączony – jeśli ustawione OutputCache  Także dla żądań z querystring  Dla własnych HttpHandlerów – włączyć User Mode Caching  http://d.com/katalog/ zamiast http://d.com/katalog  W przeciwnym wypadku HTTP 302  Zapobiega cache’owaniu 3 różnych wersji IIS Cache
    •  Podobnie jak IIS, ale VaryByParam może być custom  Np. kontekst użytkownika  Fragment Cache – OutputCache w User Control  Substitution Cache  Cała strona cache’owana, oprócz <asp:Substitution> Cache ASP.NET <body> Cached time: <%= DateTime.Now.ToString() %> <br />Page time: <asp:Substitution ID="sub" runat="server" MethodName="SubTime" /> </body> public static string SubTime(HttpContext context) { return DateTime.Now.ToString(); }
    •  Plik  Baza danych  Konieczne włączenie Service Broker Cache Dependency CacheDependency depend = new CacheDependency(this.MapPath("~/depend.txt")); this.CachePolicy.Dependency = depend; using (SqlConnection conn = new SqlConnection(cs)) { string sql = "dbo.GetInfo"; using (SqlCommand cmd = new SqlCommand(sql, conn)) { cmd.CommandType = CommandType.StoredProcedure; conn.Open(); SqlCacheDependency dep = new SqlCacheDependency(cmd); mygrid.DataSource = cmd.ExecuteReader(); mygrid.DataBind(); this.Response.AddCacheDependency(dep); } } // SqlDependency.Start() w web.config
    •  Np. AppFabric Cache Provider public class MemoryCacheProvider : OutputCacheProvider { public override object Add(string key, object entry, DateTime utcExpiry) { object result = HttpRuntime.Cache[key]; if (result == null) { this.Set(key, entry, utcExpiry); result = entry; } return result; } public override object Get(string key) { return HttpRuntime.Cache[key]; } public override void Remove(string key) { HttpRuntime.Cache.Remove(key); } public override void Set(string key, object entry, DateTime utcExpiry) { HttpRuntime.Cache.Insert(key, entry, null, utcExpiry, Cache.NoSlidingExpiration, CacheItemPriority.High, null); } } <system.web> <caching> <outputCache> <providers> <add name="MemoryCacheProvider" type="Samples.MemoryCacheProvider" /> </providers> </outputCache> </caching> </system.web>
    •  Kiedy chcemy jak najwięcej, jak najdłużej (na ile pamięć pozwoli)  Omijamy logikę Cache Policy - szybkość  Nie dla web farm  WeakReference i GC Statyczne pola – uproszczony cache public static class Weak { public static WeakReference MyItem { get; set; } public static readonly Object lockObject = new Object(); public static DataSet WeakData() { DataSet ds = null; lock (lockObject) { if (MyItem != null) ds = MyItem.Target as DataSet; if (ds == null) { ds = new DataSet(); MyItem = new WeakReference(ds); } } return ds; } }
    • IIS
    •  Domyślnie – jeden proces w3wp.exe  AppPool - kilka procesów dla kolekcji aplikacji (Web Garden)  Maximum Worker Processes  AppPool Recycling  Tracimy cache, inproc session, static, …  Domyślnie 29h  Warto zmienić na określoną godzinę lub liczbę obsłużonych requestów (NLB) App Pool
    •  Wiele AppPools  Dzielenie aplikacji na kilka puli  Błąd w jednej puli nie ma wpływu na działanie drugiej  Np. obsługa magazynu i aplikacja dla konsumentów  Web Garden  Ochrona przed błędami procesu w3wp – kilka procesów  Uwaga - context-switching między wątkami szybszy niż między procesami!  Konieczna duplikacja w pamięci danych takich jak cache czy pola static  Zalecane max 1-2 worker procesy / CPU core  Tylko, kiedy najistotniejsza ciągłość pracy Kiedy najważniejsze jest działanie
    •  ASP.NET – ISAPI Extension  Duplikacja funkcjonalności  2 odrębne pipeline’y  Np. Logowanie, auth -> IIS, później ASP.NET  Trudniejsza konfiguracja IIS6 + ASP.NET (Classic mode) Authentication Basic NTLM Anon ... Determine Handler ... SendResponse HTTP Request HTTP CGI Static File ISAPI Compre ssion Log aspnet_isapi.dll Authentication Map Handler Forms Windows ... ASPX Trace ... ...
    •  Wyodrębnione komponenty z w3core.dll  Niezależne dodawanie / usuwanie funkcjonalności  Np. auth – możemy dać własne zamiast basic / LDAP, itp..  Lekki core Architektura IIS 7 Authentication ... ExecuteHandler ... SendResponse HTTP Request HTTP Response Authorization UpdateCache ResolveCache Authentication ... Determine Handler ... SendResponse HTTP Request HTTP Response BasicNTLM Anon CGI Static File ISAPI Log Compre ssion Url Authz Output Cache Forward er Basic40+
    •  Handler HTTP  Mapowany dla rozszerzeń (np. aspx)  Migracja z html do dynamic -> można dodać mapowanie htm na ASP.NET  Moduły HTTP  ASP.NET bezpośrednio w IIS Pipeline  Np. sesja, uwierzytelnianie dla zawartości statycznej, php, itp.  Rozszerzalność przez .NET, a nie ISAPI (C++)  Usuwać niepotrzebne moduły  Kolejność typów dokumentów w IIS IIS 7 + ASP.NET - Integrated Pipeline ISAPI Authentication ... ExecuteHandler ... SendResponse Authorization UpdateCache ResolveCache HTTP Request Anon aspnet_isapi.dll Authentication Map Handler ... ... Forms Windows ASPX Trace ... Basic Compre ssion Log Static File
    •  Nowe w Windows Server 2008  Rezerwacja minimalnej ilości pamięci lub CPU dla procesów Windows System Resource Manager
    •  Bezpieczeństwo i mniej pobieranych danych  X-Powered-By – IIS  Nagłówek Server – tylko z kodu  Etag – przy web farm można wyłączyć (często jest różny dla node’ów)  X-Aspnet-Version - <httpRuntime enableVersionHeader="false"/> Usuwanie nagłówków public class HttpHeaderCleanup : IHttpModule { public void Init(HttpApplication context) { context.PreSendRequestHeaders += OnPreSendRequestHeaders; } void OnPreSendRequestHeaders(object sender, EventArgs e) { HttpResponse response = HttpContext.Current.Response; response.Headers.Remove("Server"); response.Headers.Remove("ETag"); } public void Dispose() { } }
    •  3-5% więcej CPU, ale generalnie warto  Gzip (domyślny) lub deflate (mniejszy nagłówek) Kompresja <httpCompression directory="%SystemDrive%inetpubtempIIS Temporary Compressed Files" dynamicCompressionDisableCpuUsage="100"> <scheme name="gzip" dll="%Windir%system32inetsrvgzip.dll" staticCompressionLevel="9" dynamicCompressionLevel="5" /> <scheme name="deflate" dll="%Windir%system32inetsrvgzip.dll" staticCompressionLevel="9" dynamicCompressionLevel="5" /> <staticTypes> <add mimeType="text/*" enabled="true" /> <add mimeType="message/*" enabled="true" /> <add mimeType="application/x-javascript" enabled="true" /> <add mimeType="*/*" enabled="false" /> </staticTypes> <dynamicTypes> <add mimeType="text/*" enabled="true" /> <add mimeType="message/*" enabled="true" /> <add mimeType="application/x-javascript" enabled="true" /> <add mimeType="*/*" enabled="false" /> </dynamicTypes> </httpCompression>
    •  Lepsze dla SEO  http.sys nawet przy querystringach  Krótsze URLe w odpowiedziach  Ukrywa technologię aplikacyjną  IIS (URL Rewrite) lub ASP.NET URL Rewriting
    •  Pamiętać o botach (robots.txt, sitemap.xml)  Bandwidth Throttling  Moduł BitRateThrottling do IIS  Głównie dla mediów, ale również można do innych typów danych  Np. wolniej dla botów lub kawałek dużego zdjęcia szybko Szybkość transferu public class Throttle : IHttpModule { public void Init(HttpApplication context) { context.PostRequestHandlerExecute += OnPostRequestHandlerExecute; } void OnPostRequestHandlerExecute(object source, EventArgs e) { HttpApplication application = (HttpApplication)source; HttpContext context = application.Context; HttpResponse response = context.Response; if (response.ContentType == "application/x-zip-compressed") { HttpRequest request = context.Request; if (!String.IsNullOrEmpty(request.ServerVariables["SERVER_SOFTWARE"])) { request.ServerVariables["ResponseThrottler-InitialSendSize"] = "20"; request.ServerVariables["ResponseThrottler-Rate"] = "10"; } } } public void Dispose() { } }
    • ASP.NET
    • Tradycyjne przetwarzanie żądań “thread-per-request” a.k.a. “post office” Thread pool Żądania BusyBusy Busy Busy Przy load testach – zużycie CPU niewielkie, ale długi czas odpowiedzi
    • Przetwarzanie asynchroniczne a.k.a. “restaurant” Żądania Thread pool
    •  Domyślnie 12 / CPU  Czasem zwiększenie liczby pomaga  Wiąże się z tym koszt (start, pamięć, context switch)  Ważniejsza optymalizacja istniejących  Blokujące wywołania – baza, zewnętrzne usługi, I/O  Przy obliczeniach (CPU) nie pomoże! Worker Threads
    •  Domyślnie przetwarzanie synchroniczne (1 wątek całe lifecycle)  Async point Synchronicznie i asynchronicznie
    • Asynchronicznie – web forms (APM) <%@ Page Async="true„ AsyncTimeout=„30” Language="C#" AutoEventWireup="true" CodeFile="sql-async.aspx.cs" Inherits="sql_async" %> public const string ConnString = "Data Source=.;Integrated Security=True;Async=True"; protected void Page_Load(object sender, EventArgs e) { PageAsyncTask pat = new PageAsyncTask(BeginAsync, EndAsync, null, null, true); this.RegisterAsyncTask(pat); } private IAsyncResult BeginAsync(object sender, EventArgs e, AsyncCallback cb, object state) { SqlConnection conn = new SqlConnection(ConnString); conn.Open(); SqlCommand cmd = new SqlCommand("WAITFOR DELAY '00:00:01'", conn); IAsyncResult ar = cmd.BeginExecuteNonQuery(cb, cmd); return ar; } private void EndAsync(IAsyncResult ar) { using (SqlCommand cmd = (SqlCommand)ar.AsyncState) { using (cmd.Connection) { int rows = cmd.EndExecuteNonQuery(ar); } } }
    •  Task – zaczyna od razu  APM – kolejkuje do async point Asynchronicznie – web forms (Task) public const string ConnString = "Data Source=.;Integrated Security=True;Async=True"; protected async void Page_PreRender(object sender, EventArgs e) { using (SqlConnection conn = new SqlConnection(ConnString)) { conn.Open(); using (SqlCommand cmd = new SqlCommand("WAITFOR DELAY '00:00:01'", conn)) { await Task.Factory.FromAsync<int>(cmd.BeginExecuteNonQuery, cmd.EndExecuteNonQuery, null); } } }
    •  Najlepiej agregować (np. kilka zapytań w procedurze skł.)  Wywoływane równolegle lub jedno po drugim Asynchroniczne strony – wiele zadań protected void Page_Load(object sender, EventArgs e) { // Dwa pierwsze – równolegle. Następnie BeginAsync3 (ostatni parametr PageAsyncTask) PageAsyncTask pat = new PageAsyncTask(BeginAsync1, EndAsync1, null, null, true); this.RegisterAsyncTask(pat); pat = new PageAsyncTask(BeginAsync2, EndAsync2, null, null, true); this.RegisterAsyncTask(pat); pat = new PageAsyncTask(BeginAsync3, EndAsync3, null, null, false); this.RegisterAsyncTask(pat); }
    •  Czasem potrzeba zarejestrowania zadania po zakończeniu poprzedniego  Np. drugie wywołanie usługi po zakończeniu poprzedniego  Konieczne wywołanie ExecuteRegeisteredAsyncTasks()  Można także wywołać, aby wymusić wywołanie przed async point  Dla Task – zwyczajnie jeden po drugim z await Późniejsza rejestracja private void EndAsync(IAsyncResult ar) { using (SqlCommand cmd = (SqlCommand)ar.AsyncState) { using (cmd.Connection) { int rows = cmd.EndExecuteNonQuery(ar); } } PageAsyncTask pat = new PageAsyncTask(BeginAsync2, EndAsync2, null, null, true); this.RegisterAsyncTask(pat); this.ExecuteRegisteredAsyncTasks(); }
    •  Usługi  Pliki Inne operacje protected async void Page_Load(object sender, EventArgs e) { var terra = new TerraServiceSoapClient(); Place place = new Place() { City = "Seattle", State = "WA", Country = "US" }; var result = await terra.GetPlaceFactsAsync(place); PlaceFacts facts = result.Body.GetPlaceFactsResult; this.LA.Text = String.Format("Latitude: {0:0.##}", facts.Center.Lat); this.LO.Text = String.Format("Longitude: {0:0.##}", facts.Center.Lon); } // Dla web requestów: // var r = WebRequest.Create(„http://…”); // var res = await r.GetResponseAsync(); private IAsyncResult BeginAsync(object s, EventArgs e, AsyncCallback cb, object s) { FileStream fs = new FileStream(this.Server.MapPath("csg.png"), FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.Asynchronous | FileOptions.SequentialScan); this.Data = new byte[64 * 1024]; IAsyncResult ar = fs.BeginRead(this.Data, 0, this.Data.Length, cb, fs); return ar; // dla podejścia Task: // int size = await fs.ReadAsync(this.Data, 0, this.Data.Length); }
    • Async controller – przed MVC 4 public class WeatherAsyncController : AsyncController { private string _forecastUrl = "http://weather.yahooapis.com/forecastjson?w=523920&u=c"; public void IndexAsync() { var webClient = new WebClient(); AsyncManager.OutstandingOperations.Increment(); webClient.DownloadStringCompleted += (sender, evt) => { // capture result when web service completes AsyncManager.Parameters["json"] = evt.Result; AsyncManager.OutstandingOperations.Decrement(); // Exception handling - challenge... }; // async call webClient.DownloadStringAsync(new Uri(_forecastUrl)); } public ActionResult IndexCompleted(string json) { WeatherData weather = new JavaScriptSerializer().Deserialize<WeatherData>(json); return View("Weather", weather); } }
    • Async controller – MVC 4 public class WeatherTaskAsyncController : AsyncController { private string _forecastUrl = "http://weather.yahooapis.com/forecastjson?w=523920&u=c"; public async Task<ActionResult> Index() { string json = await new WebClient().DownloadStringTaskAsync(_forecastUrl); WeatherData weather = new JavaScriptSerializer().Deserialize<WeatherData>(json); return View("Weather", weather); } }
    •  Możliwość wywołania kodu równolegle  Po wyrenderowaniu strony lub w trakcie  ThreadPool.QueueUserWorkItem()  Zużywa wątki z AppPool  1 wątek w tle + kolejka  Typowy consumer - producer  Np. logowanie (nie wymagane 100% działanie)  Bardziej krytyczne zadania – np. Service Broker lub Azure Worker + kolejka  Przy wielu wątkach – warto ReaderWriterLockSlim  EnterReadLock / EnterWriteLock (mniejsze ryzyko zakleszczenia)  Przykład Background Worker Thread
    •  InProc  Najszybsza, ale load balancer tylko sticky  Nie odporna na awarie sprzętu  StateServer  Web farm – ok, ale single point of failure  SQL (+SQL Agent do usuwania)  Możliwe przyspieszenie przez <sessionState compressionEnabled=„true” />  <%@ Page EnableSessionState=„false” @> - ale i tak update (timeout)  <%@ Page EnablesessionState=„ReadOnly” @> - mniej locków; ta sama SP, która odczytuje  Przyspieszyć zapis do logu (podział na dyski, SSD, itp.)  PartitionResolver (wybór conn str na podst id sesji) i SessionIDManager (id sesji z nr maszyny)  Mimo wszystko niezbyt skalowalne – patrz: AppFabric session provider Sesja
    • Control ID  Możliwość kontroli identyfikatorów (klienckich) kontrolek serwerowych  Control.ClientIdMode  Legacy  Static  Predictable  Inherit (domyślne dla kontrolek)  Kolekcje – ClientIDRowSuffix  Dla całej strony lub kontrolki  Ostrożnie – duplikacja, długość
    •  Możliwość zmiany markupu kontrolki serwerowej  Np. GridView nie z tabelkami, ale na floatach Control Adapter // Zamiana URL w Image na małe litery public class ImageControlAdapter : WebControlAdapter { public ImageControlAdapter() { } protected override void BeginRender(System.Web.UI.HtmlTextWriter writer) { Image image = Control as Image; if ((image != null) && !String.IsNullOrEmpty(image.ImageUrl)) { if (!image.ImageUrl.StartsWith("http") && !image.ImageUrl.StartsWith("data:")) { image.ImageUrl = this.Page.ResolveUrl(image.ImageUrl).ToLower(); } } base.BeginRender(writer); } } // adapter.browser <browsers> <browser refID="Default"> <controlAdapters> <adapter controlType="System.Web.UI.WebControls.Image" adapterType="Samples.ImageControlAdapter" /> <adapter controlType="System.Web.UI.WebControls.Panel" adapterType="Samples.NoIdControlAdapter" /> <adapter controlType="System.Web.UI.WebControls.Label" adapterType="Samples.NoIdControlAdapter" /> <adapter controlType="System.Web.UI.WebControls.HyperLink" adapterType="Samples.NoIdControlAdapter" /> </controlAdapters> </browser> </browsers>
    •  Np. wyłączenie domyślnego generowania ID Control Adapter c.d. – usuwanie ID public class NoIdControlAdapter : WebControlAdapter { protected override void Render(HtmlTextWriter writer) { PageBase page = this.Page as PageBase; if ((page != null) && page.RemoveIds && (this.Control.ClientIDMode != ClientIDMode.Static)) { HtmlTextWriter noIdwriter = new NoIdHtmlWriter(writer); base.RenderBeginTag(noIdwriter); base.RenderContents(writer); base.RenderEndTag(noIdwriter); } else { base.Render(writer); } } } public class NoIdHtmlWriter : HtmlTextWriter { public NoIdHtmlWriter(TextWriter writer) : base(writer) { } public override void AddAttribute(HtmlTextWriterAttribute key, string value) { if (key != HtmlTextWriterAttribute.Id) base.AddAttribute(key, value); } } public class PageBase : Page { protected override void OnInit(EventArgs e) { base.OnInit(e); this.RemoveIds = true; } public bool RemoveIds { get; set; } }
    •  Np. do zwracania zapisanego w bazie HTML  Bez cyklu życia ASP.NET – szybciej  Asynchronicznie – uczestniczy w każdym żądaniu!  IsReusable – czy jedna instancja może być dla wielu żądań HTTP Handler w .NET <%@ WebHandler Language="C#" Class="Handler" %> using System; using System.Data; using System.Web; using System.Data.SqlClient; public class Handler : IHttpAsyncHandler { public const string ConnString = "Data Source=.;Initial Catalog=Sample;Integrated Security=True;Async=True"; HttpContext Context { get; set; } public void ProcessRequest(HttpContext context) { } public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { this.Context = context; int fileid = 0; string id = context.Request.QueryString["id"]; if (!String.IsNullOrEmpty(id))
    •  Krótsze adresy w kodzie  http.sys caching  Łączenie np. z ASP.NET MVC (MapRoute / MapPageRoute) Routing protected void Application_Start(object sender, EventArgs e) { RouteTable.Routes.Add("Category", new Route("{category}", new PageRouteHandler("~/1.3 Routing.aspx"))); RouteTable.Routes.Add("CategoryAndPage", new Route("{category}/{page}", new PageRouteHandler("~/1.3 Routing.aspx"))); } protected void Page_Load(object sender, EventArgs e) { lblCategory.Text = RouteData.Values["category"] as string; lblPage.Text = RouteData.Values["page"] as string; }
    •  Klasyczne  this.Response.Redirect("~/pages/error.aspx", true); True – czy zakończyć request  this.Response.RedirectPermanent  this.Response.RedirectToRoute  Server.Transfer(„~/pages/error.aspx”, false)  False – czy przekazać parametry z obecnej strony  True – dla ViewState wyrzuci exception; tylko dla querystring  Cross-page postback Przekierowania <asp:Button runat="server" PostBackUrl="~/pages/otherpage.aspx" Text="Submit" />
    •  Zawartość z Render() buforowana i zwracana na koniec  Dla długotrwałych zadań może wymagać czasu  Najlepiej – z Ajax  Response.Flush() – wysłanie odpowiedzi z bufora  Przed Render() nic w nim nie ma! Wczesny flush odpowiedzi
    •  W OnPreRender  Ręczny zapis odpowiedzi  Wyrenderowanie wybranych kontrolek serwerowych  Usunięcie wyrenderowanych kontrolek (aby nie było duplikacji po Render)  Długotrwałe zadanie w OnPreRender (async) lub synchronicznie po PreRender (po async point) Wczesny flush – c.d. <html> <head id="Head1" runat="server"> <title></title> <script type="text/javascript" src="test.js"></script> </head> <body> <form id="form1" runat="server"> <div> <asp:Label runat="server" ID="test" Text="testing" /> </div> </form> </body> </html> protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); this.Response.Write("<!DOCTYPE html PUBLIC " + ""-//W3C//DTD XHTML 1.0 Transitional//EN" " + ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">n"); this.Response.Write("<html xmlns="http://www.w3.org/1999/xhtml">n"); HtmlTextWriter writer = this.CreateHtmlTextWriter(this.Response.Output); this.Header.RenderControl(writer); writer.Flush(); this.Response.Flush(); this.Controls.Remove(this.Header); Thread.Sleep(2000); }
    • Wczesny flush - odpowiedź HTTP/1.1 200 OK Cache-Control: private Transfer-Encoding: chunked Content-Type: text/html; charset=utf-8 Server: Microsoft-IIS/7.5 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Date: Fri, 03 Feb 2012 12:16:11 GMT 161 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1"> <link href="App_Themes/mkt/common.css" type="text/css" rel="stylesheet" /> <title> Testing </title> <script type="text/javascript" src="test.js"></script> </head> 146 <body> <form name="form1" method="post" action="flush1.aspx" id="form1"> <div> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE0NDMxNDM0MTlkZA0loE+taD1AMKhxDNDZZLZADwxZqGnPPbIF8Mylq4PV" /> </div> <div> <span id="test">testing</span> </div> </form> </body> </html> 0
    • Minification dla dynamicznych public class MinifyStream : Stream { private StreamWriter Writer { get; set; } private Decoder Utf8Decoder { get; set; } public MinifyStream(Stream stream) { this.Writer = new StreamWriter(stream, Encoding.UTF8); this.Utf8Decoder = Encoding.UTF8.GetDecoder(); } public override void Write(byte[] buffer, int offset, int count) { int characterCount = this.Utf8Decoder.GetCharCount(buffer, offset, count); char[] result = new char[characterCount]; int decodedCount = this.Utf8Decoder.GetChars(buffer, offset, count, result, 0); if (decodedCount <= 0) return; // ... wyfiltrowanie zbędnych znaków i zapis do this.Writer } public override void Close() { this.Writer.Flush(); this.Writer.Close(); base.Close(); } public override void Flush() { this.Writer.Flush(); } // ... } // W module HttpModule private void Sample_PostRequestHandlerExecute(Object source, EventArgs e) { HttpApplication application = (HttpApplication)source; HttpResponse response = application.Context.Response; if(response.ContentType == "text/html") response.Filter = new MinifyStream(response.Filter); }
    •  Page.IsPostBack  Rozpoznawanie odświeżenia strony Unikanie niepotrzebnej pracy protected virtual bool IsRefresh { get { return this.Request.Headers["Pragma"] == "no-cache" || this.Request.Headers["Cache-Control"] == "max-age=0"; } }
    •  Response.IsClientConnected  Czy przeglądarka jeszcze czeka na odpowiedź, czy została zamknięta?  Warto sprawdzić przed długotrwałą operacją na bazie  <compilation batch=true />  True – niewiele pakietów  False – każda strona w osobnym assembly (wolniej, ale łatwiej zmiany)  Wyłączyć debug mode!  Można wymusić w machine.config Inne <system.web> <deployment retail="true" /> </system.web>
    • Auto-start aplikacji //applicationHost.config <serviceAutoStartProviders> <add name="PrewarmMyCache" type="MyNamespace.CustomInitialization, MyLibrary" /> </serviceAutoStartProviders> public class CustomInitialization : System.Web.Hosting.IProcessHostPreloadClient { public void Preload(string[] parameters) { // Nasza inicjalizacja. } }  Dawniej – Global.asax i Application_Load  IIS 7.5 i Windows Server 2008 R2
    •  Książka Ultra-Fast ASP.NET 4.5  Książka Ultra-Fast ASP.NET  MSDN – ASP.NET Performance Zasoby
    • © 2012 Microsoft Corporation. Wszelkie prawa zastrzeżone. Microsoft, Windows oraz inne nazwy produktów są lub mogą być znakami towarowymi lub zastrzeżonymi znakami towarowymi firmy Microsoft w Stanach Zjednoczonych i innych krajach. Zamieszczone informacje mają charakter wyłącznie informacyjny. FIRMA MICROSOFT NIE UDZIELA ŻADNYCH GWARANCJI (WYRAŻONYCH WPROST LUB DOMYŚLNIE), W TYM TAKŻE USTAWOWEJ RĘKOJMI ZA WADY FIZYCZNE I PRAWNE, CO DO INFORMACJI ZAWARTYCH W TEJ PREZENTACJI.