{"id":3780,"date":"2016-05-10T07:10:45","date_gmt":"2016-05-10T05:10:45","guid":{"rendered":"https:\/\/u-labs.de\/portal\/?p=3780"},"modified":"2019-11-02T14:40:52","modified_gmt":"2019-11-02T12:40:52","slug":"iis-anwendungsinitialisierung-durch-vorabladen-von-asp-net-webanwendung-beschleunigen-teil-1","status":"publish","type":"post","link":"https:\/\/u-labs.de\/portal\/iis-anwendungsinitialisierung-durch-vorabladen-von-asp-net-webanwendung-beschleunigen-teil-1\/","title":{"rendered":"IIS: Anwendungsinitialisierung durch Vorabladen von ASP.NET Webanwendung beschleunigen (Teil 1)"},"content":{"rendered":"<p>Wird ein IIS Webserver neu gestartet, dauert es selbst bei kleinen Anwendungen zun\u00e4chst einige Sekunden, bis die erste Seite geladen ist. Dies erscheint angesichts des Umfangs sowie der Komplexit\u00e4t von ASP.NET nicht verwunderlich. Sp\u00e4testens wenn Datenbankzugriffe ins Spiel kommen l\u00e4dt die erste Anfrage st\u00f6rend langsam &#8211; Wartezeiten die sich im Bereich einer halben Minute bewegen sind keine Seltenheit. F\u00fcr die Nutzer ist dies schlecht, da kaum ein Anwender derart lange wartet &#8211; die Meisten sind nach 5 Sekunden weg.<\/p>\n<p><strong>Doch das muss nicht sein: Im folgenden Artikel erkl\u00e4ren wir, wie die Ladezeit durch eine im Vorfeld durchgef\u00fchrte&nbsp;Anwendungsinitialisierung stark reduziert werden kann. Dabei wird auf die Variante der Pseudo-Anfragen eingegangen. In einem weiteren Artikel werden wir auf codeseitiges Vorabladen eingehen.&nbsp;<\/strong><\/p>\n<h3><strong>Warum ist die erste Anfrage an eine ASP.NET Anwendung langsam?&nbsp;<\/strong><\/h3>\n<p>Das Problem liegt im Aufbau des Anwendungsstartes: Wird eine Anwendung gestartet &#8211; ob aufgrund eines Serverneustartes oder Deployments spielt dabei keine Rolle &#8211; initialisiert der IIS die Anwendung noch nicht. Die Module sind noch nicht geladen und auch Verbindungen zu Datenbanken sind noch geschlossen. Diese Vorg\u00e4nge werden erst bei der ersten HTTP-Anfrage durchgef\u00fchrt &#8211; oder anders ausgedr\u00fcckt: Wenn ein Nutzer die Seite im Browser \u00f6ffnet.<\/p>\n<p>Wir werden im folgenden ein deutlich sinnvolleres Ablaufmodell konfigurieren: Der IIS soll die Anwendung bereits beim Start m\u00f6glichst weitgehend initialisieren, um die Ladezeit zu reduzieren. Am einfachsten funktioniert dies \u00fcber den Aufruf von Seiten, die h\u00e4ufig genutzt und\/oder recht rechenintensiv sind &#8211; beispielsweise weil verschiedene Datenzugriffe erst initialisiert und in den Cache geladen werden m\u00fcssen.<\/p>\n<p>Komplett vermeiden lassen sich die Ladezeiten dadurch nat\u00fcrlich nicht. Versucht beispielsweise ein Nutzer w\u00e4hrend des Startes der Anwendung auf sie zuzugreifen, muss er bis zum Abschluss des Vorganges warten. F\u00fcr diesen Fall wollen wir dem Nutzer aber immerhin eine gesonderte Info-Seite anzeigen. Dies hat&nbsp;zwei Vorteile: Zun\u00e4chst ist der Nutzer besch\u00e4ftigt, da er den Hinweistext lie\u00dft. Bis er damit fertig ist, kann er unsere Anwendung vielleicht schon direkt verwenden. Und noch wichtiger: Er sieht \u00fcberhaupt etwas. Durch die Infoseite wei\u00df er, dass etwas passiert. Im Gegensatz zu einer ansonsten sichtbaren wei\u00dfen Seite mit kreiselnder Ladeanzeige.<\/p>\n<h3><strong>Anwendungsinitialisierung&nbsp;f\u00fcr den IIS installieren<\/strong><\/h3>\n<h4><strong>IIS 8 (Windows Server 2012)\/IIS 8.5 (Windows Server 2012 R2)<\/strong><\/h4>\n<p>In diesen beiden neueren Versionen ist die Anwendungsinitialisierung bereits ab Werk integriert. Man muss die gleichnamige Rolle lediglich \u00fcber den Server-Manager installieren. Sie ist im Pfad <strong>Webserver (IIS) &gt; Anwendungsentwicklung<\/strong> zu finden:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/u-img.net\/img\/6936Je.png\" alt=\"\"><\/p>\n<p>Nach der Installation steht sie sofort zur Verf\u00fcgung, ein Neustart ist nicht notwendig.<\/p>\n<h4><strong>IIS 7.5\/Windows Server 2008 R2<\/strong><\/h4>\n<p>Die Anwendungsinitialisierung ist hier noch kein fester Bestandteil des Betriebssystemes. Allerdings kann es&nbsp;\u00fcber das&nbsp;<a href=\"http:\/\/www.iis.net\/downloads\/microsoft\/application-initialization#additionalDownloads\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">Application Initialization Module<\/a>&nbsp;f\u00fcr den IIS 7.5 nachger\u00fcstet werden. Informationen zum IIS 7.0 unter Server 2008 (R1) konnte ich nicht finden, dies m\u00fcsste im Bedarfsfall in einer Nicht-Produktivumgebung getestet werden.<\/p>\n<h3><strong>Die Anwendungsinitialisierung &nbsp;aktivieren<\/strong><\/h3>\n<p>Nachdem die Anwendungsinitialisierung wie zuvor beschrieben \u00fcber den Server-Manager oder das Erweiterungsmodul installiert wurde, m\u00fcssen wir die Funktion zun\u00e4chst auf Anwendungsebene im IIS aktivieren. Dies k\u00f6nnen wir wahlweise \u00fcber&nbsp;der&nbsp;grafische Oberfl\u00e4che oder die Konfigurationsdatei&nbsp;<strong>applicationHost.config<\/strong>&nbsp;im Verzeichnis&nbsp; <strong>%WINDIR%\\system32\\inetsrv\\config<\/strong>&nbsp;durchf\u00fchren. Wir werden jeweils beide Wege beschreiben, sofern m\u00f6glich.<\/p>\n<p>Zun\u00e4chst \u00f6ffnen wir die erweiterten Einstellungen der Anwendung oder Seite im IIS-Manager \u00fcber einen Rechtsklick auf diese &gt;&nbsp;<strong>Website verwalten &gt; Erweiterte Einstellungen<\/strong>.&nbsp;Wichtig ist, die Eigenschaft&nbsp;<strong>Vorabladen aktiviert<\/strong> auf&nbsp;<strong>True<\/strong> zu setzen. Da wir sp\u00e4ter \u00c4nderungen am dazugeh\u00f6rigen Anwendungspool vornehmen m\u00fcssen, nutzen wir die Gelegenheit au\u00dferdem, um dessen Namen&nbsp;oben einzusehen:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/u-img.net\/img\/2624Wr.png\" alt=\"\"><\/p>\n<p><strong>Alternativ<\/strong> suchen wir in der applicationHosts.config nach dem Knoten&nbsp;<strong>system.applicationHost<\/strong>&nbsp;in der Struktur&nbsp;<strong>configuration\/configSections<\/strong>.&nbsp;Darin muss der Unterknoten zur jeweiligen Webanwendung ermittelt werden. M\u00f6chte man eine vollst\u00e4ndige Seite bearbeiten wie im folgenden Beispiel, w\u00e4hlt man die Anwendung mit dem Attribut <strong>path=&#8220;\/&#8220;<\/strong> aus. Er muss eine Eigenschaft namens&nbsp;<strong>preloadEnabled=&#8220;true&#8220;<\/strong> haben bzw. bekommen, falls diese noch nicht existiert:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&amp;lt;application path=&quot;\/&quot; applicationPool=&quot;Testpool&quot; preloadEnabled=&quot;true&quot;&amp;gt;\n<\/pre>\n<p>Nun muss noch der Startmodus des Anwendungspools ge\u00e4ndert werden. Dazu klicken wir im IIS Manager links auf&nbsp;<strong>Anwendungspools<\/strong>, machen einen Rechtsklick auf den Pool der Anwendung &#8211; in diesem Beispiel&nbsp;<strong>Testpool<\/strong> und klicken auf&nbsp;<strong>Erweiterte Einstellungen<\/strong>. Im sich nun \u00f6ffnenden Fenster nach der Eigenschaft&nbsp;<strong>Startmodus<\/strong> suchen, diese muss auf&nbsp;<strong>AlwaysRunning<\/strong> stehen:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/u-img.net\/img\/4503Ma.png\" alt=\"\"><\/p>\n<p>Wer stattdessen lieber mit der applicationHosts.config arbeiten m\u00f6chte, sucht in&nbsp;<strong>system.ApplicationHost<\/strong> nach&nbsp;<strong>applicationPools<\/strong>. Hier ist beim jeweiligen Pool auf das Setzen des&nbsp;Attributes&nbsp; <strong>startMode=&#8220;AlwaysRunning&#8220;<\/strong> zu achten:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&amp;lt;add name=&quot;Testpool&quot; managedRuntimeVersion=&quot;v4.0&quot; startMode=&quot;AlwaysRunning&quot; \/&amp;gt;\n<\/pre>\n<p>Die hier gezeigten Schritte m\u00fcssen f\u00fcr jede Seite bzw. Anwendung sowie dem jeweils dazugeh\u00f6rigen Anwendungspool durchgef\u00fchrt werden, um die Anwendungsinitialisierung nutzen zu k\u00f6nnen.<\/p>\n<h3><strong>Initialisieren einer Anwendung<\/strong><\/h3>\n<p>Ab sofort arbeiten wir auf Anwendungsebene, da sich die folgenden Einstellung pro Anwendung unterscheiden k\u00f6nnen. Eine grafische Oberfl\u00e4che gibt es f\u00fcr diese \u00c4nderungen leider nicht, sodass zwingend mit der jeweiligen Web.config im WWW-Root der Anwendung gearbeitet werden muss. &nbsp;Diese \u00f6ffnen wir nun und suchen nach dem&nbsp;<strong>system.webServer<\/strong> knoten. Dort wird ein neuer Knoten zur Anwendungsinitialisierung eingef\u00fcgt:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&amp;lt;applicationInitialization\nremapManagedRequestsTo=&quot;StartupNotice.html&quot;\nskipManagedModules=&quot;true&quot; &amp;gt;\n&amp;lt;add initializationPage=&quot;\/&quot; \/&amp;gt;\n&amp;lt;\/applicationInitialization&amp;gt;\n<\/pre>\n<p>Was&nbsp;bewirkt dieser Knoten? Mit&nbsp;<strong>remapManagedRequestsTo<\/strong> definieren wir eine statische HTML-Datei (Pfad relativ zum vom WWW-Root), die dem Nutzer angezeigt wird, w\u00e4hrend sich die Anwendung initialisiert. <strong>initializationPage<\/strong> gibt einen relativen URL-Pfad an, zu dem der IIS beim Starten eine Pseudo-Anfrage schicken soll. Dadurch wird die Seite bei allen weiteren Aufrufen durch die eigentlichen Nutzer deutlich schneller geladen. Man kann an dieser Stelle auch mehrere Seiten in jeweils einem <strong>add<\/strong>-Element angeben:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&amp;lt;applicationInitialization\nremapManagedRequestsTo=&quot;StartupNotice.html&quot;\nskipManagedModules=&quot;true&quot; &amp;gt;\n&amp;lt;add initializationPage=&quot;\/SlowController\/Action&quot; \/&amp;gt;\n&amp;lt;add initializationPage=&quot;\/AnotherSlowController\/ComplexAction&quot; \/&amp;gt;\n&amp;lt;\/applicationInitialization&amp;gt;\n<\/pre>\n<h3><strong>Praxistest<\/strong><\/h3>\n<p>Um zu testen ob unsere Optimierung wirklich funktioniert, gen\u00fcgt es, den Anwendungspool zu <i>recyceln<\/i>. Dabei startet der IIS alle Arbeitsprozesse neu, d.H. unsere Anwendung muss komplett neu initialisiert werden. Damit das Ergebnis nicht durch das noch laufende Neuladen des IIS verf\u00e4lscht wird, warten wir in beiden Testf\u00e4llen nach dem recyceln, bis sich die CPU-Last danach wieder legt:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/u-img.net\/img\/2393Fy.png\"><\/p>\n<p>Nun ist die CPU im Leerlauf und die Anwendung wurde vom IIS initialisiert. Die Aktualisierungsgeschwindigkeit des Taskmanagers steht in den Diagrammen auf <i>Hoch<\/i>, um ein m\u00f6glichst genaues Bild der verursachten Last zu erhalten. Unsere Testanwendung soll mehrere Razor-Ansichten rendern und ausgeben. Das Laden dieser Testseite ist mit 700 ms (davon 23 ms Generierungszeit) erfreulich z\u00fcgig und verursacht kaum sichtbare CPU-Last:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/u-img.net\/img\/2755Re.png\"><\/p>\n<p>Zum Vergleich der erste Aufruf unserer Testseite ohne die hier gezeigten Optimierungen. Auch hier wird die gleiche Seite zum ersten mal nach dem Recyceln des App-Pools aufgerufen. Wie erwartet wird die Anwendung erst bei der ersten Anfrage initialisiert, sodass die Prozessorlast an dieser Stelle deutlich h\u00f6her ist:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/u-img.net\/img\/8871Ko.png\"><\/p>\n<p>Vollst\u00e4ndig geladen ist die Seite erst nach <strong>6,5 Sekunden <\/strong> &#8211; Der Nutzer muss hier also rund 10x l\u00e4nger auf den Seiteninhalt warten! Noch extremer wird der Unterschied bei umfangreichen Anwendungen, die beispielsweise Datenbanktreiber initialisieren m\u00fcssen. Hier kommen schnell 20 Sekunden und mehr zusammen, die weniger als 1 Sekunde gegen\u00fcberstehen.<\/p>\n<h3><strong>Fazit<\/strong><\/h3>\n<p>Die erste HTTP-Anfrage an eine ASP.NET Anwendung kann durch die Anwendungsinitialisierung drastisch beschleunigt werden, wodurch das Nutzererlebnis deutlich verbessert wird. In den folgenden Artikeln werden wir diesen Ansatz ausweiten, damit vor allem datenbanklastigere Anwendungen m\u00f6glichst effektiv davon profitieren.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wird ein IIS Webserver neu gestartet, dauert es selbst bei kleinen Anwendungen zun\u00e4chst einige Sekunden, bis die erste Seite geladen ist. Dies erscheint angesichts des Umfangs sowie der Komplexit\u00e4t von ASP.NET nicht verwunderlich. Sp\u00e4testens wenn Datenbankzugriffe ins Spiel kommen l\u00e4dt die erste Anfrage st\u00f6rend langsam &#8211; Wartezeiten die sich im Bereich einer halben Minute bewegen &#8230;<\/p>\n","protected":false},"author":5,"featured_media":6420,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[61,63],"tags":[336,70,449,66],"class_list":["post-3780","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-softwareentwicklung","category-windows-server","tag-asp-net","tag-performance","tag-windows-server-2008r2","tag-windows-server-2012"],"_links":{"self":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/3780","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/comments?post=3780"}],"version-history":[{"count":55,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/3780\/revisions"}],"predecessor-version":[{"id":6422,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/3780\/revisions\/6422"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/media\/6420"}],"wp:attachment":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/media?parent=3780"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/categories?post=3780"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/tags?post=3780"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}