2011. szeptember 25., vasárnap

Gyorsan válaszoló Azure Web Role Application

Sokak számára okoz fejfájást a IIS recycling. Kis idő után az alkalmazásunk leáll, majd egy új user request éleszti. Ez kisebb forgalmú web alkalmazásoknál egész kellemetlen lehet. Egy kis cache töltögetés, vagy hasonló tevékenység őrületbe kergeti a kedves felhasználót a túloldalon. Engem is…

Érdekes, hogy mennyire jól konfigurálható, hogy az alkalmazásunk milyen szabályok teljesülése esetén legyen leállítva. Ezzel szemben nincs semmilyen normális infrastruktúra arra, hogy az alkalmazás újra el is induljon. Pontosabban a IIS 7.5-ig nem volt.

Ez az IIS verzió a Windows Server 2008 R2-ben debütált, így ha használni akarjuk a WebRole-unkban az új lehetőséget, akkor mindenképpen R2 operációs rendszert kell hozzárendelnünk a Role-hoz.

A fent említett szolgáltatás az “Auto-Start” elnevezésre hallgat, ami máris segít, hogy elkeveredjünk és esetleg összetévesszük az application pool AutoStart beállításával. Nagyon jó a megoldás, mert nem úgy dolgozik, hogy a recycling lenyomja az appunkat, hanem előbb meghívja a warm-upot egy új worker processben, majd ha az véget ér, akkor állítja le a korábbi workert. ScottGu rövid bemutató cikkét elolvasva arra jutottam, hogy ezt pedig én beállítom az Azure alkalmazásomra is.

Az IIS beállításainak módosítására több út is van. Ezek közül csak a programozott eljárást mutatom be.

A cikket elolvasva látható, hogy nem bonyolult a funkció életre keltése, egyszerűen csak editáljuk az applicationHost.config fájlt, implementáljuk a WarmUp kódot és minden príma… Na igen, de azért az Azure-ban ilyeneket nem csinálunk...

Az alábbi kód egy komplett megoldás arra, hogy a WebRole egyik Site-ját Auto-Start-ossá tegyük. De szép mondat!

Gyors lépésről lépésre áttekintés a megoldás használatához.

  1. A WebRole projekthez hozzá kell adni az alábbi referenciát:
    %WindowsDir%\System32\inetsrv\Microsoft.Web.Administration.dll. Ez az assembly tartalmazza az IIS adminisztrációhoz szükséges logikát.
  2. Be kell állítani a Microsoft.Web.Administration referencia Copy Local projekt tulajdonságát True-ra.
  3. Minden ServiceConfiguration fájlban be kell állítani a ServiceConfiguration elemben a osFamly attribútumot 2-re. Ez azt jeleni, hogy Windows Server 2008 R2 rendszeren fog futni a role.
    <ServiceConfiguration serviceName="FastAzureApp" xmlns="" 
                          osFamily="2" osVersion="*">
  4. Ugyanebben a konfigurációban be kell állítani a következőt, hogy legyen jogunk “állítgatni”:
    <Runtime executionContext="elevated" />
  5. Hozzá kell adni a WebRole-hoz egy osztályt, amely a warm up során lesz példányosítva és meghívva. A példában FastMvcWebRole.Helpers.WarmUp
  6. Meg kell valósítani a WarmUp osztályban a IProcessHostPreloadClient interfészt.
    public class WarmUp : IProcessHostPreloadClient
    {
        public void Preload(string[] parameters)
        {
            Trace.WriteLine("Warm up.");
     
            //Inicializáló kód jöhet ide...
     
            Trace.WriteLine("Warm up done.");
        }
    }
  7. Az alábbi metódust hozzáadva a WebRole osztályhoz, beállítható az IIS Auto-Start feature:

    /// <summary>
    /// Az site és az application pool beállításainak módosítása az IIS konfigurációkban
    /// </summary>
    /// <param name="siteName">A paraméter ServiceDefinition.csdef fájl Site name-re utal</param>
    /// <param name="keyName">A serviceAutoStart elem egyedi kulcsa (IIS szinten)</param>
    /// <param name="methodReference">A Warm Up metódus referenciája "Namespace.Metodus, Assembly" formátumban</param>
    private void SetupIIS(string siteName, string keyName, string methodReference)
    {
        using (var serverManager = new ServerManager())
        {
            var siteRealName = string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteName);
     
            var site = serverManager.Sites[siteRealName];
            if (site == null)
                throw new Exception(String.Format("Site not found: {0}", siteRealName));
     
            var siteApplication = site.Applications.FirstOrDefault();
            if (siteApplication == null)
                throw new Exception(string.Format("Site Application (first) not found:{0}", siteRealName));
     
            var appPoolName = siteApplication.ApplicationPoolName;
            var appPool = serverManager.ApplicationPools[appPoolName];
     
            //Egy óra tétlenség után legyen recycle
            appPool.ProcessModel.IdleTimeout = TimeSpan.FromMinutes(60);
     
            //Periodikusan is legyen recycle
            appPool.Recycling.PeriodicRestart.Time = TimeSpan.FromHours(29);
     
            //Tegyük az Application Pool-t Always Running módba
            appPool["startMode"] = "AlwaysRunning";
     
            //Állítsuk be a site-on is az AutoStart szolgáltatás engedélyezését
            siteApplication["serviceAutoStartEnabled"] = true;
     
            //Adjuk meg az AutoStart providerünk kulcsát
            siteApplication["serviceAutoStartProvider"] = keyName;
     
            //Adjunk hozzá egy elemet (saját providerünk) az serviceAutoStartProviders kollekcióhoz
            var config = serverManager.GetApplicationHostConfiguration();
            var serviceAutoStartProvidersSection = config.GetSection("system.applicationHost/serviceAutoStartProviders");
            var serviceAutoStartProvidersCollection = serviceAutoStartProvidersSection.GetCollection();
     
            var serviceAutoStartProviderElement =
                serviceAutoStartProvidersCollection.SingleOrDefault(f => f.ElementTagName == "add" && (string)f.Attributes["name"].Value == keyName);
     
            if (serviceAutoStartProviderElement != null)
                serviceAutoStartProvidersCollection.Remove(serviceAutoStartProviderElement);
     
            serviceAutoStartProviderElement = serviceAutoStartProvidersCollection.CreateElement("add");
            serviceAutoStartProviderElement["name"] = keyName;
            serviceAutoStartProviderElement["type"] = methodReference;
            serviceAutoStartProvidersCollection.Add(serviceAutoStartProviderElement);
     
            serverManager.CommitChanges();
        }
    }
  8. Hívjuk meg a SetupIIS metódust az OnStart override-olt metódusból.
    public override bool OnStart()
    {
        SetupIIS("Web", "FastMvcWebRoleWarmUp", 
            "FastMvcWebRole.Helpers.WarmUp, FastMvcWebRole");
     
        return base.OnStart();
    }

Ennyi az egész.
A forráskód elérhető a CodePlex-en: http://martonrusko.codeplex.com címen, vagy letölthető innen:

2011. szeptember 6., kedd

Azure Table storage a gyakorlatban

Aki ismerkedik az Azure-ral – még, ha régi motoros is – óhatatlanul szembesül a klasszikus iskolai kérdéssel: Mi az a …, és mire használható. Ilyen “jelenség” az Azure Table Service is, ami a 3 újfajta tárolási lehetőség egyike.

Az ismerkedés első napján máris sikerült kifektetni a Development Storage Emulátort. Órákig refaktoráltam a kódot, próbáltam kitalálni, hogy vajon mi lehet az oka, hogy az emulátor nem válaszol (!) semmilyen kérésre egy entitás beszúrása után. Pontosabban fogalmazva: egy sikertelen beszúrás után, amelyre a teljesen üres táblát tartalmazó emulátor válasza, hogy

System.Data.Services.Client.DataServiceClientException: 
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/
dataservices/metadata
"> <code>InternalError</code> <message xml:lang="hu-HU">Server encountered an internal error.
Please try again after some time
.</message> </error>

Először megszüntettem minden öröklődést. Már csak egy darab árva entitásom volt, amely egyszerűbb volt, mint a Hello World példa, mégis a fenti üzeneteket kaptam, és a storage emulátor többet nem volt képes válaszolni. Egyszerűen meghalt. Az Azure Storage Explorer a következőt tudta csak mondani:

image 

Minden ilyen próbálkozás után használhatatlanná vált az egész development storage emulator. Szerencsére van egy ilyen gomb: Reset…

image

A gombot megnyomva “lenullázhatjuk” az egész storage-ünket…Azért ez egy éles alkalmazásnál nem nyugtatna meg… Szerencsére csak fejlesztgetünk egyelőre.

Lényeg, hogy a sok-sok megkötés mellett van még egy: a storage emulátor nem bírja a DateTime.MaxValue értéket… Ennek az oka alapvetően egyszerű: a Table Service UTC dátumokkal dolgozik. Így, ha egyszerűen a MaxValue értéket állítjuk be a változó értékének, akkor a DateTime.Kind tulajdonsága Unspecified lesz, azaz meghatározatlan. Ilyen esetben az időt konvertálni fogja a rendszer UTC-be. Ami ugye ilyen módon később van, mint a megengedett maximum.

Az eset rém egyszerűen elkerülhető, ha az alábbi módon adjuk meg a maximum dátumot: DateTime.MaxValue.ToUniversalTime(); Illetve általában figyelnünk kell arra, hogy minden dátum át lesz konvertálva UTC-be. Bár ez nem hiszem, hogy a MaxValue érték 2 órás körzetén kívül bárkit is zavarna Magyarországon.

Az ilyen apróságok mellett a véleményem továbbra is az, hogy az  Azure Table Storage nagyon jó dolog. Erőltetem is rá a fejlesztést, annak ellenére, hogy még egy kicsit még fapados.

2011. szeptember 2., péntek

Azurálódás

Nem gondoltam volna másfél évvel ezelőtt, amikor egy konferencián először hallottam a Windows Azure-ről, hogy egyszer csak én is azt fontolgatom, hogy használjam. Sőt, ma már úgy érzem, hogy kifejezetten versenyképes.

Nyilván nem lehet összevetni olyan szolgáltatásokkal, amelyek évi 10-15 ezer forintért kínálnak “99.9999%-os” rendelkezésre állású tárhelyeket, majd úgy kifekszenek mint a MÁV jegyértékesítő rendszere az 1 db szerverével.

Szóval, belevágtam és előfizettem. Elég sok mindent meg kell ismerni ahhoz, hogy az ember kijelenthesse, hogy tudja, érti az Azure-t.

Nagyon érdekes érzés, hogy mint fejlesztő, egészen másképp látom a világot, ha az Azure-rel dolgozom. Elengedhetetlen, hogy az ember kicsit üzletemberként gondolkozzon, amikor nekikezd egy probléma technikai megoldásához.

Ha az offline világban dolgozunk, előfordulhat, hogy lazán kezeljük a könnyen elérhető erőforrásokat. Ilyen példul az SQL Server adatbázis. Ugye az Express változatok megjelenése óta még arra is SQL-t használunk, amire nem kellett. Nos, ez a Azure világában kicsit másképp néz ki. Itt 1 GB SQL adatbázis 9.99 USD/hó, 5 GB pedig 49,95 USD/hó. Hoppá. Mindjárt beindulnak az optimalizációs gondolatok a fejben…

Beugrik az embernek, hogy vannak itt egyéb lehetőségek is. Például: Mi is az a Azure Table Service? – kérdezi magától… Megnézi az árát, és egyből úgy érzi, hogy esetleg bizonyos dolgokat csak ebben kellene tárolni, mert itt az 1 GB storage ára csak 0,15 USD. De egyből oda is kell figyelni ismét, mert OK, hogy olcsó a store, de van ám itt tranzakciós díj is: 0,01 USD/10.000 tranzakció. Na, az meg mi a…

Szóval ez egy más világ. Érdemes sokat olvasgatni, de ha van egy kis pénzed, annál érdekesebb kipróbálni. Igen részletesen számláznak. Itt nem duma, hogy mindenért fizetsz Mosolygó arc Itt egy screenshot a számlázási elszámolásról…

Azure_billing

Érdemes részletesen megismerni a számlázási egységeket, illetve az azokkal kapcsolatos okosságokat. 1-2 sor kód cseréje komoly megtakarításokhoz vezethet. Éppen ezért nem csak a vezetőknek érdemes ismerni a számlázási adatokat, hanem a fejlesztőknek is kötelező.

Például a fent említett Table megoldás esetén 100 updatet beküldhetünk úgy, hogy az 100 tranzakcióként jelenjen meg a számlán, és úgy is hogy 1 árva darabként. Tényleg másképp kell gondolkodni.

Az alábbi blogbejegyzés részletesen taglalja a számlázás mikéntjét. Ajánlom mindenkinek:

Brad Calder: Understanding Windows Azure Storage Billing – Bandwidth, Transactions, and Capacity

Ez nem a legolcsóbb szolgáltatás, ha mindent nézünk. Nem is arra lett kitalálva, hogy egy 3 oldalas weblapot tegyünk ki, amit 100-an látogatnak évente. Ezt a szolgáltatást azzal az esettel lehet összevetni, amikor legalább egy dedikát virtuális gépre van szükségünk. Itt már érezhetően versenyképes. Ha saját szerverüzemeltetésben gondolkodunk, akkor meg szerintem előnyösebb. Érdemes számolgatni.

Illetve nem csak számolgatni, hanem belegondolni abba, hogy mekkora lehetőség van egy ilyen comboboxban:

Azure_choose_region

Néhány kattintással határozzuk meg, hogy hol működjön az alkalmazásunk, SQL Serverünk, vagy a storage-ünk. Nem kell foglalkozni semmilyen adminisztrációval, rendeléssel, operációs rendszer menedzsmenttel, csak az alkalmazásunkkal.

Érdekes aspektus az is, hogy olyan dolgokat tudunk kipróbálni, amelyeket egyszerűen nem vennénk meg, mert túl drága. Itt a minden szolgáltatás elszámolása használat alapú. A számítás óránként, míg a storage a napi átlagok alapján számolódik. De az SQL Server is ilyen. Szóval, ha 1 napig kell egy adatbázis, akkor egy napot fizetsz.

Érdekes, hogy ha egy új Hosted Service-t hozol létre (kvázi ez egy virtuális gép), akkor kb 3-4 perc és rendelkezésre áll a gép, saját fix IP címmel. Ez utóbbi nagyon tetszett. Ha mondjuk tényleg csak kipróbálni akarod, akkor egy ExtraSmall egység óránkánt 0,15 USD. 1-2 óra alatt kipróbálod, majd törlöd a service-t. Ezzel szemben egy VPS szolgáltatónál ki kell fizetned legalább 1 hónapot. Persze előbb meg kell keresni az optimális szolgáltatót, regisztrálni, kifizetni az egy hónapot, várni a szolgáltatás aktiválására, majd belevágni az ismeretlenbe… Ugyanezt az Azure a fenti 1 db dialogbox kitöltésével megoldja.

Le vagyok nyűgözve az Azure lehetőségeitől. Ajánlom mindenkinek! A jövőben megosztom a további tapasztalataimat is.

Referenciák:

Azure kezdőlap
Windows Azure Pricing Calculator
Brad Calder: Understanding Windows Azure Storage Billing – Bandwidth, Transactions, and Capacity