Tag Archives: IIS

Alkalmazások bemelegítése IIS 7.5 és 8.0 alatt

Mint minden szoftvernél, webalkalmazások esetén is előfordulhat, hogy az alkalmazásnak induláskor inicializálnia kell magát, adatbázis kapcsolatokat kell felépítenie, cache-t kell feltöltenie, sőt az összes olyan rendszer- és platform komponenst is inicializálnia kell, amelyre ráépül. A .NET keretrendszerre épülő alkalmazások esetén ehhez még hozzájön a just-in-time fordítás, mellyel összességében az alkalmazáshoz érkező első kérés kiszolgálása zavaróan elhúzódhat.

Erre a problémára a hagyományos megoldás az, hogy a felhasználói kéréseket megelőzve mi küldjük be az első kérést a webalkalmazásnak, így mire a valódi felhasználók megérkeznek, már a bemelegített, villámgyors honlappal fognak találkozni. Ezzel a megoldással sajnos legalább három probléma van:

  • Meg kell írnunk a kliens szkriptet, ami HTTP kérésekkel bombázza a webalkalmazásunkat.
  • Gondoskodnunk kell arról, hogy a bemelegítő szkriptünk az alkalmazáskészlet minden indulásakor lefusson.
  • IIS 7.5 előtt a felhasználóktól érkező kérések megelőzhetik a bemelegítő szkriptet (architekturális korlát).

Szerencsére a Windows 7-ben és a Windows Server 2008 R2-ben megjelent IIS 7.5 ezen a területen jelentősen fejlődött. A kulcskérdés az, hogy az alkalmazás minek a hatására kezdi el inicializálni magát?

IIS 6 és 7.0 esetén miután megtörtént az alkalmazáskészlet inicializálása, a rendszer készen áll a kérések fogadására, ám az alkalmazás inicializálását valójában az első kérés beérkezése indítja el (katt a teljes képért):

IIS_7_timeline

Ennek következtében a felhasználónak meg kell várnia nem csak az általa beküldött kérés feldolgozását, hanem még a teljes webalkalmazás inicializálását is, és csak utána kapja meg a választ. A helyzetet súlyosbítja, hogy ha van már egy korábbi futó alkalmazáskészlet, akkor azt a webszerver leállítja, miután az új alkalmazáskészlet inicializálása megtörtént – csakhogy ekkor még az új worker processben a webalkalmazás nem áll készen! A felhasználónak tehát nem csak, hogy sokat kell várakoznia, de még az is feltűnik neki, hogy a korábbi gyors rendszer (az előző alkalmazáskészlet már be volt melegítve) hirtelen egy kérés erejéig jelentősen lelassult.

Az IIS 7.5-ben a fejlesztők változtattak a webszerver architektúráján, így az alkalmazáskészlet inicializálása után automatikusan megtörténhet az alkalmazás inicializálása is:

IIS_75_timeline

Ráadásul itt már igazi átlapolt újraindításról (overlapped recycling) beszélhetünk, mert a régi alkalmazáskészlet leállítása csak az után történik meg, hogy az újban az alkalmazás inicializálása is megtörtént, így az első kérést küldő felhasználó egyáltalán nem fogja észrevenni, hogy valójában egy új worker process dolgozza fel a kérését.

Ez a módszer azonban feltételezi azt, hogy a webszerver tudja, hogyan kell a webalkalmazást inicializálni, ami nyilván nem lehet mindig igaz, hiszen nagyon sokféle rendszer létezik. Valójában ez csak úgy tud működni, ha a webalkalmazás fel van készítve erre a funkcionalitásra, ami természetesen egyelőre nem minden alkalmazásra igaz.

Ha az alkalmazásunk még nincs felkészítve az IIS 7.5 új alkalmazáskészlet architektúrájára, akkor nem marad más választásunk, mint a korábbiakhoz hasonlóan HTTP kérésekkel bemelegíteni az alkalmazást. Itt azonban már van arra lehetőségünk, hogy ezek a bemelegítő kérések még az előtt érjék el az alkalmazást, hogy az előző alkalmazáskészlet leállna vagy a felhasználótól kérések érkeznének. Ráadásul egy új modul segítségével még a kérések beküldéséről sem nekünk kell gondoskodnunk.

Korábban az IIS 7.5-höz a Microsoft kiadott egy Application Warm-Up nevű opcionális bővítményt, ami képes volt a beállított URL-ekre a megfelelő időben beküldeni az inicializáló kéréseket. Ez a modul bár nagyon hasznos volt (főként a SharePoint üzemeltetők imádták), még a béta időszak alatt kiderült róla, hogy nem minden szempontból felel meg a Microsoft hosszútávú terveinek, ezért sajnos levették a honlapról és azóta nem volt kész megoldásunk. Az IIS 8-ban azonban beépítetten megjelent ez a funkció Application Initialization néven:

IIS_AppInit_Install

Szerencsére az IIS 7.5 számára is elérhetővé tették ezt a funkciót opcionális bővítmény formájában, aminek most a Release Candidate változata tölthető le innen:

A telepítés nagyon egyszerű next-next-finish, de utána ne keresgéljünk semmit az IIS Managerben, egyelőre nem tartozik ehhez a modulhoz GUI. A beállításokat vagy a config fájlokban, vagy az IIS Manager Configuration Editor moduljával tehetjük meg.

Első lépésként azt célszerű beállítanunk, hogy az application pool mindig fusson (hiszen a leállási időt akarjuk minimalizálni):

 <add name="MyPool" startMode="AlwaysRunning" ... /> 

Ennek hatására a Windows Process Activation Service (WAS) mindig el fogja indítani az alkalmazáskészletet.

A következő lépés a preloading engedélyezése az alkalmazásnál, szintén az applicationHost.config fájlban:

 <application path="/" applicationPool="MyPool" preloadEnabled="true" ... />


Ez fogja kiváltani az első kérést, ami inicializálja az alkalmazást. Már csak az van hátra, hogy meghatározzuk, mi történjen ennél a kérésnél. Ezt legszebben az alkalmazás web.configjában tehetjük meg a system.webServer szekcióban:

<applicationInitialization remapManagedRequestsTo="Loading.html" 
                           skipManagedModules="true" 
                           doAppInitAfterRestart="true"> 
<
add initializationPage="/Default.aspx" />
</
applicationInitialization>

Az add sorokban több URL-t is felsorolhatunk, ahova  kéréseket fog küldeni a modul az alkalmazás inicializálása alatt. A remapManagedRequestsTo attribútumban pedig egy olyan oldalt adhatunk meg, amit válaszként fog visszaküldeni a rendszer az inicializálás ideje alatt (ez persze csak akkor izgalmas, ha nincs átlapolódás az előző alkalmazáskészlettel).

Ezzel készen is vagyunk. Persze lehet még tovább cifrázni, például küldhetünk vissza többféle várakozó képernyőt is, ha összekapcsoljuk az Application Initialization modult az ingyenesen letölthető URL Rewrite modullal. Ezt az teszi lehetővé, hogy az AppInit modul beállítja az APP_WARMING_UP szerver változó értékét 1-re arra az időre, amíg az inicializálás tart. Ebben a cikkben erre is szerepel példa: http://learn.iis.net/page.aspx/1089/iis-80-application-initialization/

 

Advertisements

IIS 8.0 Express Beta

Olyan ez, mint az augusztusi csillaghullás, csak úgy záporoznak az égből a béták, minden napra jut belőlük pár. Ezúttal az IIS csapat eresztett meg egyet, mégpedig az IIS 8.0 Express bétáját, ami innen tölthető le. Érdekes, hogy néhány kivételével sikerült minden IIS funkciót megvalósítaniuk az Express változatban, ami nem csak hogy Windows 8-on, hanem Windows Vista SP1-től kezdve minden Windows verzión fut! XP tulajdonosok maradhatnak a 7.5-nél (ez jön egyébként a mostani Visual Studio 11 bétával is).

Az újdonságok és korlátozások listája megtalálható a Readme-ben.

IIS8

A “nagy” IIS telepítése Windows 8-on egyébként sima ügy, bár kicsit tovább tartott, mint korábban Windows  7-en, ami valószínűleg annak volt köszönhető, hogy pár csomagot a Windows Update-ről rántott le frissen (ami viszont dicséretes). Utána viszont gondban van az ember, mert sehogy sem lehet elindítani az IIS Managert, hiszen amíg a kereső le nem indexeli, addig hiába keresgélünk rá a Search Baron. Aki nem akar 1-2 percet várni, az kapcsolja be a Charm Baron a Start Screen Settings-ei között a Show Administrative Tools kapcsolót, ami után megjelenik az IIS szokásos ikonja a Metros Start képernyőn.

Az IIS Managert elindítva a szokásos látvány fogad:

IIS8-Manager

Úgy tűnik, hogy nincsenek eget rengető újdonságok, ami részben igaz is, hiszen a fő újdonságok az egyes funkciók skálázhatóságával kapcsolatosak, ami elsősorban shared hosting környezetben lesz fontos (pl. több ezer site egy gépen). A lényeg tehát a motorháztető alatt van, de azért itt-ott kicsillan egy-egy igazi gyöngyszem, például:

IIS8-SNI

 

Technorati-címkék: ,

A Connect igenis működik

Időnként én is szoktam szapulni a Microsoft Connect szolgáltatását, hiszen nem egyszer előfordul, hogy az ember beküld egy hibajelenséget, amire visszajön a By Design, Won’t fix, Postponed vagy valami hasonló válasz. De legyünk őszinték, ki nem állított még be hasonló választ a saját bug trackerében? És akkor egyszer csak jön egy Closed as Fixed válasz, ami elsöpör minden korábbi kellemetlen élményt.

November elején írtam arról, hogy milyen szomorú, hogy az IIS ApplicationPooldentity nem lehet SQL Server Agent job owner, mert elbukik a felhasználói fiók ellenőrzésén. Akkor be is küldtem ezt a hibát a Connectre, hogy legalább nyoma maradjon. Egy héten belül jött a standard “thank you – we are investigating” válasz, majd kértek további infókat, aztán a szokásos csönd, el is felejtettem a dolgot. Aztán most kapom a szívderítő választ:

Hey György
I just wanted to update you that the issue was fixed and you should see it in next major release, you actually can test it on SQL12 RC0 pre-release.
Thanks
Alex Grach [MSFT]

Mindezt úgy, hogy mindössze heten szavaztak erre a hibára (köszönöm mindenkinek). Like!

Szóval íme, itt az élő példa, hogy a Connect != /dev/null. Ha hibát találtok, küldjétek be!

 

Technorati-címkék: ,,

COMException: Illegal operation attempted on a registry key

COM is loveA COMException egy igazi klasszikus rémálom. Egyrészt ijesztő (“jajj, mit kezdjünk most ezzel?”), másrészt Champollion legyen a talpán, aki rájön, hogy igazából mi a baj. Persze a második gátlástalanul erősíti az elsőt.

Íme egy példa illusztrációként: adott egy webszerver, rajta egy ASP.NET-es alkalmazás, ami Active Directory-hoz kapcsolódik. A webhely tökéletesen működik, de a szerverre kerül egy másik web application is, ami után a DirectoryEntry konstruktor nem tud a címtárhoz kapcsolódni, helyette inkább elszáll ezzel a hibával:

System.Runtime.InteropServices.COMException (0x800703FA): Illegal operation attempted on a registry key that has been marked for deletion.

Pedig én aztán nem csináltam semmit, sem ezzel az alkalmazással, sem a címtárral, sem a registry-vel! Azért elég zavarba ejtő, az Event Logban persze semmi hasznos, a hibaüzenetben még kevésbé.

Hát akkor rajta, elő a Process Monitorral, az majd megmondja, hogy kinek melyik registry kulccsal van baja –  persze csak miután az ember leszűrte és átnyálazta a sok ezer sornyi logot. Azért előtte egy gyors apppool recycle, hátha… És bejött, azóta semmi baja a szervernek.

COM is not love.

 

Technorati-címkék: ,,

IIS AppPool kontra SQL Server Agent

Írtam már párszor, hogy nagy híve vagyok az IIS 7-ben bevezetett ApplicationPoolIdentity-nek, éppen ezért mindig elszomorít, amikor valahol kiderül, hogy nem lehet használni. Legutóbb az SQL Server Agentnél futottunk bele egy problémába.

Van egy webalkalmazásunk, ami AppPoolIdentity-vel kapcsolódik az SQL Serverhez. Mivel az alkalmazás SQL Server Agent jobokat kezel, ezért a fiók annak rendje és módja szerint megkapta az SQLAgentUserRole és az SQLAgentReaderRole szerepköröket az msdb adatbázisban. Ennek ellenére az sp_add_job tárolt eljárás az alábbi hibával elszáll:

Msg 14234, Level 16, State 1, Line 3
The specified ‘@owner_login_name’ is invalid (valid values are returned by sp_helplogins [excluding Windows NT groups]).

Ilyenkor az ember jóhiszeműen követi az utasításokat, lekérdezi a helyes értékeket az sp_helplogins meghívásával és szomorúan tapasztalja, hogy a kérdéses fiók szerepel a listában. WTF?

Semmi gond, hiszen az sp_add_job kódját könnyen meg lehet találni Management Studioval, amiből rövid bogarászás után kiderül, hogy ez a tárolt eljárás meghívja az sp_verify_job SP-t és valójában az dobja a hibát ennél a sornál:

IF (@owner_sid IS NULL) OR (EXISTS (SELECT *
                                 FROM master.dbo.syslogins
                                 WHERE (sid = @owner_sid)
                                   AND (isntgroup <> 0)))
BEGIN
  SELECT @res_valid_range = FORMATMESSAGE(14203)
  RAISERROR(14234, -1, -1, '@owner_login_name', @res_valid_range)
  RETURN(1) -- Failure
END

Innen már nem kell messzire menni: kérdezzük le a master.dbo.syslogins tartalmát és látni fogjuk, hogy a kérdéses sorban az isntgroup értéke bizony 1. Deadlock Mérges

Jobb ötlet híján feldobtam bugként a Connectre, ha ez szerinted is probléma, szavazz rá:

Microsoft Connect: IIS ApplicationPoolIdentity cannot be a SQL Agent job owner

 

URL: csak egy maradhat

Régóta ismert tény, hogy a keresőmotorok azt szeretik, ha egy oldalra mindig ugyanazzal az URL-lel hivatkozunk. A SEO tesztelő programok, például az IIS SEO Toolkit, de még a WebMatrixba épített ellenőrző is, panaszkodnak, ha egy oldalhoz több URL-t talál:

The page contains multiple canonical formats.

A problémára lehet megoldás a 301 Moved Pemanently átirányítás, amit például az IIS UrlRewrite modullal egyszerűen meg is lehet csinálni. De mi van akkor, ha nincs a kezemben a szerver és nem telepíthetek mindenféle bővítményeket.

Akkor marad az inline trükközés, amit szerencsére tiszteletben tartanak a keresőmotorok:

<link rel="canonical" href="az-igazi-url" />

Az az-igazi-url helyére jöhet abszolút vagy akár relatív URL is.

Bővebben a Google WebMaster Central oldalán: http://www.google.com/support/webmasters/bin/answer.py?answer=139394

Durva ASP.NET-IIS breaking change a Server SP1-ben

Korábban írtam róla, hogy a .NET 4 GDR Update milyen jó, mert sok egyéb mellett például egy a Windows Server 2008 R2 Service Pack 1 által behozott problémát is megold. Nos, a rossz hír az, hogy nem mindent, ráadásul egy olyan breaking change éled fel tőle, ami nem kevés alkalmazást érinthet.

Van egy szunnyadó változás az ASP.NET környékén, ami egészen addig nem okoz gondot, amíg nem telepítünk egy olyan javítást (QFE), ami aktivizálja ezt a funkciót, például a nemrég megjelent WebMatrix+MVC3+… csomagot vagy éppen a Windows Server/7 SP1-et. A változás a kiterjesztés nélkül URL-ek kezelését érinti, és az a lényeg, hogy mostantól a “/” URL-re érkező kéréseket is az ASP.NET kapja el először és nem az IIS. (Ezt korábban is meg lehetett csinálni a runAllManagedModulesForallRequests beállításával.)

Korábban ez úgy működött, hogy a “/” URL-je jövő kérést az IIS-ben lévő DefaultDocumentModule kapta el és irányította át például a “/default.aspx” címre, átzavarva a kérést a szerver egész HTTP csővezetékén. Ebben a második körben harapott rá a kérésre az ASP.NET, és döntötte el a location és authorization beállítások alapján, hogy kiszolgálja-e a kérést.

Mostantól (.NET 4 + QFE) viszont megfordul a kép: mivel a kérésben nincs kiterjesztés, ezért először az ASP.NET kezdi el feldolgozni azt és ahogy azt elvárjuk, érvényesíti a location és authorization tag-ben lévő beállításokat (átirányít vagy kiszolgálja a kérést).

A rossz hír, hogy a friss .NET 4 GDR Update-nek erre az egészre egy fikarcnyi hatása sincs, ez a változás megmarad utána is. Ez by design viselkedés, ami azért szükséges, hogy az ASP.NET előbb végezze el a dolgát a kiterjesztés nélküli URL-ekre, mint hogy az IIS-ben lévő StaticFile handler lefutna.  A kellemetlenség igazi forrására akkor derül fény, amikor megnézzük az applicationHost.config fájlban, hogy mit is tartalmaz ez a handler:

<add name="StaticFile" path="*" verb="*"
modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule" … />

Bizony, egy handlerben van a StaticFileModule és a DefaultDocumentModule is, tehát esély sincs rá, hogy a DefaultDocumentModule előbb dolgozza fel a kérést, mint az ASP.NET egy olyan gépen, ahol a kiterjesztés nélküli URL-ek kezelése be van kapcsolva  Szomorú arc

Ha nincs szükség a kiterjesztés nélküli URL-ekre, akkor az alábbi web.config módosítással orvosolható a probléma:

<system.webServer>
  <handlers>
    <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
    <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
    <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
  </handlers>

  <!-- Ha az IIS még panaszkodik "disallowed configuration sections" miatt,
       akkor még ez is kellhet: -->
  <!--
    <validation validateIntegratedModeConfiguration="false" />
  -->
</system.webServer>