Es ist unglaublich, wie einfach es ist, Webanwendungen mit ASP.NET zu schreiben. Aufgrund dieser Einfachheit nehmen sich viele Entwickler nicht die Zeit, ihre Anwendungen für eine bessere Leistung zu strukturieren. In diesem Artikel behandle ich 10 Tipps zum Schreiben leistungsstarker Webanwendungen. Ich würde diese Vorschläge jedoch nicht auf ASP.NET-Anwendungen beschränken, da diese Anwendungen nur Teil einer Webanwendung sind. Dieser Artikel ist nicht als endgültiger Leitfaden zur Leistungsoptimierung von Webanwendungen gedacht – ein ganzes Buch könnte dieses Thema nicht ohne weiteres abdecken. Betrachten Sie diesen Artikel als einen guten Ausgangspunkt.
Bevor ich zum Workaholic wurde, habe ich gerne Klettern gemacht. Vor jedem großen Anstieg schaue ich mir zunächst die Routen in den Reiseführern genau an und lese die Empfehlungen früherer Besucher. Aber egal wie gut der Guide ist, Sie benötigen echte Klettererfahrung, bevor Sie sich an einen besonders anspruchsvollen Aufstieg wagen. Ebenso können Sie nur dann lernen, wie man leistungsstarke Webanwendungen schreibt, wenn Sie mit der Behebung von Leistungsproblemen oder dem Betrieb einer Website mit hohem Durchsatz konfrontiert sind.
Meine persönlichen Erfahrungen stammen aus meiner Arbeit als Infrastrukturprogrammmanager in der ASP.NET-Abteilung bei Microsoft, wo ich www.ASP.NET leitete und verwaltete und bei der Entwicklung der Community-Server mitwirkte, einem von mehreren bekannten
ASP.NET-Anwendungen (ASP.NET-Foren, .Text und nGallery kombiniert in einer Plattform). Ich bin mir sicher, dass einige der Tipps, die mir geholfen haben, auch Ihnen helfen werden.
Sie sollten darüber nachdenken, Ihre Anwendung in mehrere logische Schichten zu unterteilen. Möglicherweise haben Sie den Begriff „3-stufige (oder n-stufige) physische Architektur“ gehört. Hierbei handelt es sich typischerweise um vorgeschriebene Architekturansätze, die die Funktionalität physisch zwischen Prozessen und/oder Hardware trennen. Wenn das System erweitert werden muss, kann problemlos weitere Hardware hinzugefügt werden. Mit Prozess- und Maschinensprüngen sind jedoch Leistungseinbußen verbunden, die vermieden werden sollten. Versuchen Sie daher, wenn möglich, ASP.NET-Seiten und die zugehörigen Komponenten zusammen in derselben Anwendung auszuführen.
Aufgrund der Codetrennung und der Grenzen zwischen den Ebenen führt die Verwendung von Webdiensten oder Remoting zu einer Leistungseinbuße von 20 % oder mehr.
Die Datenschicht ist etwas anders, da es normalerweise besser ist, dedizierte Hardware für die Datenbank zu verwenden. Allerdings sind die Kosten für den Prozesssprung in die Datenbank immer noch hoch, sodass die Leistung der Datenschicht das erste Problem ist, das Sie bei der Optimierung des Codes berücksichtigen sollten.
Bevor Sie sich mit Leistungskorrekturen für Ihre Anwendung befassen, stellen Sie zunächst sicher, dass Sie ein Profil Ihrer Anwendung erstellen, um die spezifischen Probleme zu identifizieren. Wichtige Leistungsindikatoren, beispielsweise solche, die den Prozentsatz der für die Garbage Collection benötigten Zeit darstellen, sind auch nützlich, um herauszufinden, wo eine Anwendung ihre Hauptzeit verbringt. Doch wo die Zeit verbracht wird, ist oft sehr unintuitiv.
In diesem Artikel werden zwei Arten von Leistungsverbesserungen beschrieben: große Optimierungen (z. B. die Verwendung von ASP.NET-Caching) und kleine Optimierungen, die sich wiederholen. Diese kleinen Optimierungen sind manchmal besonders interessant. Sie nehmen eine kleine Änderung an Ihrem Code vor und gewinnen viel Zeit. Bei großen Optimierungen kann es zu einem großen Anstieg der Gesamtleistung kommen. Mit kleinen Optimierungen sparen Sie möglicherweise nur ein paar Millisekunden für eine bestimmte Anfrage, aber über alle Anfragen jeden Tag hinweg addiert, kann es eine enorme Verbesserung sein.
Leistung der Datenschicht
Wenn es um die Optimierung der Anwendungsleistung geht, gibt es einen Messstabtest, mit dem Sie Ihre Arbeit priorisieren können: Greift der Code auf die Datenbank zu? Wenn ja, mit welcher Frequenz? Beachten Sie, dass derselbe Test auch auf Code angewendet werden kann, der Webdienste oder Remoting verwendet, dieser Artikel behandelt diese jedoch nicht.
Wenn in einem bestimmten Codepfad eine Datenbankanforderung erforderlich ist und Sie der Meinung sind, dass Sie zuerst andere Bereiche optimieren müssen (z. B. String-Manipulation), stoppen Sie diesen Dipstick-Test und führen Sie ihn dann durch. Wenn Ihre Leistungsprobleme nicht gravierend sind, ist es eine gute Idee, etwas Zeit damit zu verbringen, die für die Datenbank aufgewendete Zeit, die Menge der zurückgegebenen Daten und die Häufigkeit der Roundtrips zur und von der Datenbank zu optimieren.
Schauen wir uns unter Berücksichtigung dieser allgemeinen Informationen zehn Tipps an, die zur Verbesserung der Anwendungsleistung beitragen können. Zuerst werde ich über die Änderungen sprechen, die den größten Unterschied machen könnten.
Tipp 1 – Mehrere Ergebnissätze zurückgeben
Sehen Sie sich Ihren Datenbankcode sorgfältig an, um festzustellen, ob mehrere Anforderungspfade in die Datenbank vorhanden sind. Jeder dieser Roundtrips reduziert die Anzahl der Anfragen pro Sekunde, die die Anwendung bedienen kann. Durch die Rückgabe mehrerer Ergebnismengen in einer Datenbankanforderung können Sie die für die Kommunikation mit der Datenbank erforderliche Gesamtzeit einsparen. Gleichzeitig wird das System dadurch skalierbarer, da die Arbeit des Datenbankservers bei der Verwaltung von Anfragen reduziert wird.
Obwohl es möglich ist, dynamisches SQL zu verwenden, um mehrere Ergebnismengen zurückzugeben, ist meine bevorzugte Methode die Verwendung gespeicherter Prozeduren. Es gibt einige Diskussionen darüber, ob sich die Geschäftslogik in der gespeicherten Prozedur befinden sollte, aber ich denke, es wäre besser, wenn die Logik in der gespeicherten Prozedur die zurückgegebenen Daten einschränken könnte (die Größe des Datensatzes verringern und den Zeitaufwand reduzieren). Dies sollte bevorzugt werden.
Wenn Sie eine stark typisierte Geschäftsklasse mithilfe einer SqlCommand-Instanz und ihrer ExecuteReader-Methode auffüllen, können Sie den Ergebnismengenzeiger durch den Aufruf von NextResult nach vorne verschieben. Abbildung 1 zeigt eine Beispielsitzung mit Typklassen zum Auffüllen mehrerer ArrayLists. Wenn Sie nur die Daten aus der Datenbank zurückgeben, die Sie benötigen, wird die Speicherzuweisung auf dem Server weiter reduziert.
Abbildung 1 Extrahieren mehrerer Ergebnismengen aus einem DataReader
// die erste Ergebnismenge lesen
reader = command.ExecuteReader();
// liest die Daten aus dieser Ergebnismenge
while (reader.Read()) {
Lieferanten.Add(PopulateSupplierFromIDataReader(reader));
}
// Nächste Ergebnismenge lesen
reader.NextResult();
// liest die Daten aus dieser zweiten Ergebnismenge
while (reader.Read()) {
products.Add(PopulateProductFromIDataReader(reader));
}
Tipp 2 – Paginierter Datenzugriff
ASP.NET DataGrid verfügt über eine nette Funktion: Unterstützung für Datenpaginierung. Wenn Paging im DataGrid aktiviert ist, wird eine feste Anzahl von Datensätzen gleichzeitig angezeigt. Darüber hinaus wird unten im DataGrid eine Paging-Benutzeroberfläche angezeigt, um die Navigation zwischen Datensätzen zu erleichtern. Mit der Paging-Benutzeroberfläche können Sie vorwärts und rückwärts durch die angezeigten Daten navigieren und eine feste Anzahl von Datensätzen gleichzeitig anzeigen.
Es gibt auch eine kleine Wendung. Für die Paginierung mit dem DataGrid müssen alle Daten an das Raster gebunden sein. Wenn Ihre Datenschicht beispielsweise alle Daten zurückgeben muss, filtert das DataGrid alle angezeigten Datensätze basierend auf der aktuellen Seite. Wenn beim Durchblättern des DataGrids 100.000 Datensätze zurückgegeben werden, werden bei jeder Anforderung 99.975 Datensätze verworfen (bei einer Seitengröße von 25 Datensätzen). Wenn die Anzahl der Datensätze steigt, leidet die Anwendungsleistung, da mit jeder Anfrage immer mehr Daten gesendet werden müssen.
Eine hervorragende Möglichkeit, leistungsfähigeren Paginierungscode zu schreiben, ist die Verwendung gespeicherter Prozeduren. Abbildung 2 zeigt eine beispielhafte gespeicherte Prozedur zum Paging der Orders-Tabelle in der Northwind-Datenbank. Kurz gesagt, alles, was Sie an dieser Stelle tun müssen, ist, den Seitenindex und die Seitengröße zu übergeben. Anschließend wird der entsprechende Ergebnissatz berechnet und zurückgegeben.
Abbildung 2 Durchblättern der Bestelltabelle
PROZEDUR ERSTELLEN nordwind_OrdersPaged
(
@PageIndex int,
@PageSize int
)
ALS
BEGINNEN
DECLARE @PageLowerBound int
DECLARE @PageUpperBound int
DECLARE @RowsToReturn int
– Legen Sie zuerst die Zeilenanzahl fest
SET @RowsToReturn = @PageSize * (@PageIndex + 1)
SET ROWCOUNT @RowsToReturn
– Legt die Seitengrenzen fest
SET @PageLowerBound = @PageSize * @PageIndex
SET @PageUpperBound = @PageLowerBound + @PageSize + 1
– Erstellen Sie eine temporäre Tabelle zum Speichern der Auswahlergebnisse
TABELLE #PageIndex ERSTELLEN
(
IndexId int IDENTITY (1, 1) NICHT NULL,
OrderID int
)
– In die temporäre Tabelle einfügen
EINFÜGEN IN #PageIndex (Bestell-ID)
WÄHLEN
Bestell-ID
AUS
Bestellungen
BESTELLEN NACH
OrderID DESC
– Gesamtzahl der Rückgaben
SELECT COUNT(OrderID) FROM Orders
– Seitenergebnisse zurückgeben
WÄHLEN
O.*
AUS
Befehle O,
#PageIndex SeitenIndex
WO
O.OrderID = PageIndex.OrderID AND
PageIndex.IndexID > @PageLowerBound UND
PageIndex.IndexID < @PageUpperBound
BESTELLEN NACH
PageIndex.IndexID
END
Im Community-Server haben wir eine Paging-Server-Steuerung geschrieben, um das gesamte Daten-Paging abzuschließen. Wie Sie sehen werden, verwende ich das in Tipp 1 besprochene Konzept, um zwei Ergebnismengen aus einer gespeicherten Prozedur zurückzugeben: die Gesamtzahl der Datensätze und die angeforderten Daten.
Die Gesamtzahl der zurückgegebenen Datensätze kann je nach ausgeführter Abfrage variieren. Beispielsweise kann eine WHERE-Klausel verwendet werden, um die zurückgegebenen Daten einzuschränken. Um die Gesamtzahl der in der paginierten Benutzeroberfläche angezeigten Seiten zu berechnen, müssen Sie die Gesamtzahl der zurückzugebenden Datensätze kennen. Wenn beispielsweise insgesamt 1.000.000 Datensätze vorhanden sind und Sie sie mithilfe einer WHERE-Klausel auf 1.000 Datensätze filtern möchten, muss die Paging-Logik die Gesamtzahl der Datensätze kennen, um die Paging-Benutzeroberfläche korrekt darzustellen.
Tipp 3 – Verbindungspooling
Das Einrichten einer TCP-Verbindung zwischen einer Webanwendung und SQL Server kann ein sehr ressourcenintensiver Vorgang sein. Entwickler bei Microsoft können schon seit längerem Connection Pooling nutzen und so Datenbankverbindungen wiederverwenden. Anstatt für jede Anfrage eine neue TCP-Verbindung aufzubauen, bauen sie nur dann eine neue Verbindung auf, wenn keine Verbindungen im Verbindungspool vorhanden sind. Wenn die Verbindung geschlossen wird, kehrt sie zum Verbindungspool zurück, wo sie die Verbindung zur Datenbank aufrechterhält, anstatt die TCP-Verbindung vollständig zu zerstören.
Natürlich müssen Sie vorsichtig sein, wenn es zu undichten Verbindungen kommt. Wenn Sie mit der Verwendung Ihrer Verbindungen fertig sind, schließen Sie diese unbedingt. Um es noch einmal zu wiederholen: Ganz gleich, was jemand über die Garbage Collection im Microsoft® .NET Framework sagt, stellen Sie sicher, dass Sie explizit „Close“ oder „Dispose“ für eine Verbindung aufrufen, wenn Sie mit deren Verwendung fertig sind. Vertrauen Sie nicht darauf, dass die Common Language Runtime (CLR) Verbindungen zu vorgegebenen Zeiten für Sie löscht und schließt. Obwohl die CLR letztendlich die Klasse zerstört und das Schließen der Verbindung erzwingt, gibt es keine Garantie dafür, wann die Garbage Collection des Objekts tatsächlich erfolgt.
Um Connection Pooling optimal zu nutzen, müssen einige Regeln befolgt werden. Öffnen Sie zuerst die Verbindung, führen Sie den Vorgang aus und schließen Sie dann die Verbindung. Wenn es sein muss, öffnen und schließen Sie die Verbindung mehrmals pro Anfrage (am besten unter Anwendung von Tipp 1), aber lassen Sie die Verbindung nicht die ganze Zeit offen und leiten Sie sie nicht mit verschiedenen Methoden ein und aus. Zweitens verwenden Sie dieselbe Verbindungszeichenfolge (und dieselbe Thread-ID, wenn Sie die integrierte Authentifizierung verwenden). Wenn Sie nicht dieselbe Verbindungszeichenfolge verwenden und beispielsweise die Verbindungszeichenfolge basierend auf dem angemeldeten Benutzer anpassen, erhalten Sie nicht denselben Optimierungswert, den der Verbindungspool bereitstellt. Wenn Sie die integrierte Authentifizierung nutzen und zudem eine große Anzahl von Benutzern imitieren, sinkt auch die Effizienz des Verbindungspools deutlich. Die .NET CLR-Datenleistungsindikatoren können hilfreich sein, wenn Sie Leistungsprobleme im Zusammenhang mit dem Verbindungspooling aufspüren möchten.
Immer wenn Ihre Anwendung eine Verbindung zu einer Ressource herstellt, beispielsweise zu einer Datenbank, die in einem anderen Prozess ausgeführt wird, sollten Sie die Optimierung durchführen, indem Sie sich auf die Zeit konzentrieren, die zum Herstellen einer Verbindung zu dieser Ressource benötigt wird, auf die Zeit, die zum Senden oder Abrufen von Daten benötigt wird, und auf die Anzahl der Roundtrips. Die Optimierung jeglicher Art von Prozesssprüngen in Ihrer Anwendung ist der erste Punkt, um eine bessere Leistung zu erzielen.
Die Anwendungsschicht enthält die Logik, um eine Verbindung zur Datenschicht herzustellen und Daten in sinnvolle Klasseninstanzen und Geschäftsprozesse umzuwandeln. Beispielsweise ein Community-Server, auf dem Sie die Foren- oder Threads-Sammlung füllen, dort Geschäftsregeln anwenden (z. B. Berechtigungen) und vor allem Caching-Logik ausführen möchten.
Tipp 4 – ASP.NET-Caching-API
Bevor Sie eine Zeile Anwendungscode schreiben, müssen Sie zunächst die Anwendungsschicht strukturieren, um die Caching-Funktionen von ASP.NET optimal zu nutzen.
Wenn Ihre Komponente in einer ASP.NET-Anwendung ausgeführt werden soll, müssen Sie lediglich einen Verweis auf System.Web.dll in das Anwendungsprojekt einfügen. Wenn Sie auf den Cache zugreifen müssen, verwenden Sie die HttpRuntime.Cache-Eigenschaft (auf dieses Objekt kann auch über Page.Cache und HttpContext.Cache zugegriffen werden).
Es gibt verschiedene Regeln für das Zwischenspeichern von Daten. Erstens: Wenn die Daten voraussichtlich mehrfach verwendet werden, ist dies eine gute Alternative zur Verwendung von Caching. Zweitens: Wenn die Daten generisch und nicht spezifisch für eine bestimmte Anfrage oder einen bestimmten Benutzer sind, sind sie auch ein guter Kandidat für die Zwischenspeicherung. Wenn die Daten spezifisch für den Benutzer oder die Anfrage sind, aber eine lange Lebensdauer haben, können sie dennoch zwischengespeichert werden, dies wird jedoch möglicherweise nicht sehr oft verwendet. Drittens wird die Regel oft übersehen, dass man manchmal zu viel zwischenspeichern kann. Normalerweise sollten Sie auf einem x86-Computer Prozesse mit nicht mehr als 800 MB privater Bytes ausführen, um das Risiko von Fehlern aufgrund von unzureichendem Arbeitsspeicher zu verringern. Daher sollte der Cache begrenzt sein. Mit anderen Worten: Sie können das Ergebnis einer Berechnung möglicherweise wiederverwenden. Wenn diese Berechnung jedoch 10 Parameter erfordert, versuchen Sie möglicherweise, 10 Permutationen zwischenzuspeichern, was zu Problemen führen kann. Eine der häufigsten Anfragen nach ASP.NET-Unterstützung sind Fehler wegen unzureichendem Arbeitsspeicher, die durch übermäßiges Caching verursacht werden, insbesondere bei großen Datensätzen.
Caching verfügt über mehrere großartige Funktionen, die Sie kennen müssen. Erstens implementiert der Cache einen Algorithmus, der die letzte Verwendung anzeigt und es ASP.NET ermöglicht, das Bereinigen des Caches zu erzwingen – also automatisch das Entfernen nicht verwendeter Elemente aus dem Cache –, wenn der Speicher weniger effizient läuft. Zweitens unterstützt der Cache abgelaufene Abhängigkeiten, deren Ablauf erzwungen werden kann. Zu diesen Abhängigkeiten gehören Zeit, Schlüssel und Dateien. Zeit wird oft verwendet, aber mit ASP.NET 2.0 wird ein neuer und leistungsfähigerer Invalidierungstyp eingeführt: Datenbank-Cache-Invalidierung. Es bezieht sich auf das automatische Löschen von Elementen im Cache, wenn sich die Daten in der Datenbank ändern. Weitere Informationen zur Datenbank-Cache-Ungültigmachung finden Sie in der Kolumne „Dino Esposito Cutting Edge“ im MSDN Magazine vom Juli 2004. Um die Architektur des Caches zu verstehen, sehen Sie sich das Diagramm unten an.
Tipp 6 – Hintergrundverarbeitung
Der Weg zum Code sollte so schnell wie möglich sein, oder? Es kann vorkommen, dass Sie das Gefühl haben, dass eine Aufgabe, die bei jeder Anfrage oder einmal alle n Anfragen ausgeführt wird, viele Ressourcen erfordert. Beispiele hierfür sind das Versenden von E-Mails oder das Analysieren und Validieren eingehender Daten.
Bei der Analyse der ASP.NET-Foren 1.0 und der Neuarchitektur des Inhalts, aus dem der Community-Server besteht, stellten wir fest, dass das Hinzufügen neuer Posting-Codepfade sehr langsam war. Jedes Mal, wenn ein neuer Beitrag hinzugefügt wird, muss die Anwendung zunächst sicherstellen, dass keine doppelten Beiträge vorhanden sind. Anschließend muss sie den Beitrag mithilfe eines Filters für „schlechte Wörter“ analysieren, das gepostete Zeichen-Emoticon analysieren, den Beitrag markieren und indizieren und das hinzufügen Posten Sie, wenn Sie dazu aufgefordert werden. Gehen Sie zur entsprechenden Warteschlange, validieren Sie den Anhang und senden Sie nach der endgültigen Veröffentlichung sofort eine E-Mail-Benachrichtigung an alle Abonnenten. Offensichtlich ist da viel dabei.
Nach einer Recherche stellte sich heraus, dass die meiste Zeit für die Indizierung der Logik und den E-Mail-Versand aufgewendet wurde. Das Indizieren von Beiträgen ist ein sehr zeitaufwändiger Vorgang, und es wurde festgestellt, dass die integrierte System.Web.Mail-Funktionalität eine Verbindung zu einem SMYP-Server herstellt und dann kontinuierlich E-Mails sendet. Wenn die Anzahl der Abonnenten für einen bestimmten Beitrag oder Themenbereich steigt, dauert die Ausführung der AddPost-Funktion immer länger.
Eine E-Mail-Indizierung ist nicht für jede Anfrage erforderlich. Idealerweise würden wir diesen Vorgang gerne stapelweise durchführen, indem wir 25 Beiträge gleichzeitig indizieren oder alle E-Mails alle fünf Minuten versenden. Wir haben uns entschieden, Code zu verwenden, den wir zuvor als Prototyp für die Datencache-Invalidierung verwendet hatten, der letztendlich in Visual Studio® 2005 verwendet wurde.
Die Timer-Klasse im System.Threading-Namespace ist sehr nützlich, aber im .NET Framework nicht sehr bekannt, zumindest unter Webentwicklern. Nach der Erstellung ruft diese Timer-Klasse den angegebenen Rückruf in einem konfigurierbaren Intervall für einen Thread im ThreadPool auf. Dies bedeutet, dass Sie Ihren Code so einrichten können, dass er ohne eingehende Anforderungen an die ASP.NET-Anwendung ausgeführt wird, was ideal für die Hintergrundverarbeitung ist. In diesem Hintergrundprozess können Sie auch Vorgänge wie die Indizierung oder den E-Mail-Versand durchführen.
Allerdings gibt es bei dieser Technologie mehrere Probleme. Wenn die Anwendungsdomäne deinstalliert wird, wird diese Timer-Instanz ihre Ereignisse nicht mehr auslösen. Da die CLR außerdem einen strengen Standard für die Anzahl der Threads pro Prozess hat, kann es Situationen geben, in denen der Server stark ausgelastet ist und der Timer möglicherweise keine Threads zum Abschließen hat, was in gewissem Maße zu Verzögerungen führt. ASP.NET versucht, das Risiko dafür zu minimieren, indem es eine bestimmte Anzahl von Threads im Prozess verfügbar hält und nur einen Teil der gesamten Threads für die Anforderungsverarbeitung verwendet. Dies kann jedoch ein Problem sein, wenn Sie viele asynchrone Vorgänge haben.
Für diesen Code ist hier nicht genügend Platz, Sie können jedoch ein lesbares Beispiel unter www.rob-howard.net herunterladen. Schauen Sie sich die Folien und Demos der Blackbelt TechEd 2004-Präsentation an.
Tipp 7 – Zwischenspeicherung der Seitenausgabe und Proxyserver
ASP.NET ist Ihre Präsentationsschicht (oder sollte Ihre Präsentationsschicht sein); es besteht aus Seiten, Benutzersteuerelementen, Serversteuerelementen (HttpHandlers und HttpModules) und den von ihnen generierten Inhalten. Wenn Sie über eine ASP.NET-Seite verfügen, die Ausgaben generiert (HTML, XML, Bilder oder andere Daten), und wenn Sie diesen Code bei jeder Anfrage ausführen, generiert er dieselbe Ausgabe, dann verfügen Sie über ein Tool, mit dem A Tolle Alternative zum Seitenausgabe-Caching.
Fügen Sie diese Inhaltszeile oben auf der Seite hinzu: <%@ Page OutputCache VaryByParams="none" Duration="60" %>
Sie können die Ausgabe für diese Seite einmal effizient generieren und sie dann bis zu 60 Sekunden lang mehrmals wiederverwenden. Zu diesem Zeitpunkt wird die Seite erneut ausgeführt und die Ausgabe wird erneut dem ASP.NET-Cache hinzugefügt. Dieses Verhalten kann auch mithilfe einiger programmgesteuerter Low-Level-APIs erreicht werden. Es gibt mehrere konfigurierbare Einstellungen für das Ausgabe-Caching, beispielsweise die gerade erwähnte Eigenschaft VaryByParams. VaryByParams wird nur angefordert, ermöglicht Ihnen aber auch die Angabe von HTTP-GET- oder HTTP-POST-Parametern zum Ändern von Cache-Einträgen. Legen Sie beispielsweise einfach VaryByParam="Report" fest, um die Ausgabe für „default.aspx?Report=1“ oder „default.aspx?Report=2“ zwischenzuspeichern. Zusätzliche Parameter können durch Angabe einer durch Semikolons getrennten Liste angegeben werden.
Viele Menschen wissen nicht, dass ASP.NET-Seiten bei Verwendung von Ausgabe-Caching auch einige HTTP-Header generieren, die sich nach dem Caching-Server befinden, wie sie beispielsweise von Microsoft Internet Security and Acceleration Server oder Akamai verwendet werden. Nach dem Festlegen des HTTP-Cache-Headers können Dokumente auf diesen Netzwerkressourcen zwischengespeichert und Client-Anfragen ohne Rückkehr zum Ursprungsserver erfüllt werden.
Daher wird die Verwendung von Seitenausgabe-Caching Ihre Anwendung nicht effizienter machen, aber möglicherweise die Belastung des Servers verringern, da nachgeschaltete Caching-Technologien das Dokument zwischenspeichern. Natürlich handelt es sich dabei möglicherweise nur um anonyme Inhalte. Sobald diese nachgelagert sind, werden Sie diese Anfragen nie wieder sehen und Sie können keine Authentifizierung mehr durchführen, um den Zugriff darauf zu verhindern.
Tipp 8 – Führen Sie IIS 6.0 aus (verwenden Sie es einfach für das Kernel-Caching)
Wenn Sie nicht IIS 6.0 (Windows Server? 2003) verwenden, entgehen Ihnen einige großartige Leistungsverbesserungen in Microsoft-Webservern. In Tipp 7 habe ich das Ausgabe-Caching besprochen. In IIS 5.0 werden Anforderungen über IIS und dann in ASP.NET weitergeleitet. Beim Caching empfängt das HttpModule in ASP.NET die Anfrage und gibt den Inhalt des Caches zurück.
Wenn Sie IIS 6.0 verwenden, finden Sie eine nette kleine Funktion namens Kernel-Cache, die keine Codeänderungen an ASP.NET erfordert. Wenn von ASP.NET eine Anforderung zur Ausgabezwischenspeicherung gestellt wird, erhält der IIS-Kernel-Cache eine Kopie der zwischengespeicherten Daten. Wenn eine Anfrage von einem Netzwerktreiber kommt, empfängt der Treiber auf Kernelebene (ohne Kontextwechsel in den Benutzermodus) die Anfrage, schreibt die zwischengespeicherten Daten in die Antwort, sofern sie zwischengespeichert sind, und schließt dann die Ausführung ab. Das bedeutet, dass Sie unglaubliche Leistungsergebnisse erzielen, wenn Sie Kernelmodus-Caching mit IIS- und ASP.NET-Ausgabecaching verwenden. Während der Entwicklung von ASP.NET in Visual Studio 2005 war ich als Programmmanager für die ASP.NET-Leistung verantwortlich. Die Entwickler erledigen die spezifische Arbeit, aber ich kann jeden Tag die gesamte Berichterstattung sehen. Die Ergebnisse des Kernelmodus-Cache sind immer die interessantesten. Das häufigste Merkmal ist, dass das Netzwerk mit Anfragen/Antworten überflutet wird, während IIS nur mit etwa 5 % CPU-Auslastung läuft. Das ist schockierend! Es gibt natürlich noch andere Gründe, IIS 6.0 zu verwenden, aber Kernelmodus-Caching ist der offensichtlichste.
Tipp 9 – Verwenden Sie die Gzip-Komprimierung
Obwohl die Verwendung von gzip nicht unbedingt einen Trick zur Serverleistung darstellt (da Sie möglicherweise einen Anstieg der CPU-Auslastung feststellen), kann die Verwendung der gzip-Komprimierung die Anzahl der vom Server gesendeten Bytes verringern. Dies führt zu einer wahrgenommenen Erhöhung der Seitengeschwindigkeit und einer geringeren Bandbreitennutzung. Abhängig davon, welche Daten gesendet werden, wie stark sie komprimiert werden können und ob der Client-Browser dies unterstützt (IIS sendet gzip-komprimierte Inhalte nur an Clients, die die Gzip-Komprimierung unterstützen, z. B. Internet Explorer 6.0 und Firefox), kann Ihr Server dienen Weitere Anfragen. Tatsächlich erhöht sich fast jedes Mal, wenn Sie die zurückgegebene Datenmenge reduzieren, die Anzahl der Anfragen pro Sekunde.
Die Gzip-Komprimierung ist in IIS 6.0 integriert und ihre Leistung ist viel besser als die in IIS 5.0 verwendete gzip-Komprimierung, was eine gute Nachricht ist. Wenn Sie versuchen, die gzip-Komprimierung in IIS 6.0 zu aktivieren, können Sie die Einstellung leider möglicherweise nicht im Eigenschaftendialog von IIS finden. Das IIS-Team hat eine hervorragende GZIP-Funktionalität in den Server integriert, aber vergessen, eine administrative Benutzeroberfläche einzuschließen, um diese zu aktivieren. Um die GZIP-Komprimierung zu aktivieren, müssen Sie tief in die XML-Konfigurationseinstellungen von IIS 6.0 eintauchen (damit Sie nicht schwach werden). Mein Dank geht übrigens an Scott Forsyth von OrcsWeb, der mir geholfen hat, dieses Problem mit dem auf OrcsWeb gehosteten www.asp.net-Server anzusprechen.
In diesem Artikel werden die Schritte nicht beschrieben. Bitte lesen Sie den Artikel von Brad Wilson unter IIS6-Komprimierung. Es gibt auch einen Wissensdatenbankartikel zum Aktivieren der Komprimierung für ASPX unter ASPX-Komprimierung in IIS aktivieren. Sie sollten jedoch beachten, dass dynamische Komprimierung und Kernel-Caching aufgrund einiger Implementierungsdetails in IIS 6.0 nicht gleichzeitig vorhanden sein können.
Tipp 10 – Status der Serversteuerungsansicht
View State ist ein interessanter Name für ASP.NET, der einige Statusdaten in versteckten Ausgabefeldern der generierten Seite speichert. Wenn die Seite an den Server zurückgesendet wird, kann der Server diese Ansichtszustandsdaten analysieren, validieren und wieder auf den Kontrollbaum der Seite anwenden. Die Statusanzeige ist eine sehr leistungsstarke Funktion, da sie die Beibehaltung des Status beim Client ermöglicht und zum Speichern dieses Status keine Cookies oder Serverspeicher erforderlich sind. Viele ASP.NET-Serversteuerelemente verwenden den Ansichtsstatus, um Einstellungen beizubehalten, die während Interaktionen mit Seitenelementen erstellt wurden, z. B. das Speichern der aktuellen Seite, die beim Paginieren von Daten angezeigt wird.
Die Verwendung des Ansichtsstatus hat jedoch auch einige Nachteile. Erstens erhöht es die Gesamtlast auf der Seite jedes Mal, wenn sie bereitgestellt oder angefordert wird. Zusätzlicher Overhead entsteht auch beim Serialisieren oder Deserialisieren von Ansichtsstatusdaten, die an den Server zurückgesendet werden. Schließlich erhöht der Ansichtsstatus die Speicherzuweisung auf dem Server.
Mehrere Serversteuerelemente neigen dazu, den Ansichtsstatus zu überbeanspruchen, selbst wenn er nicht benötigt wird. Das bekannteste davon ist das DataGrid. Das Standardverhalten der ViewState-Eigenschaft ist aktiviert, Sie können es jedoch auf Steuerelement- oder Seitenebene deaktivieren, wenn Sie es nicht benötigen. Setzen Sie innerhalb des Steuerelements einfach die Eigenschaft „EnableViewState“ auf „false“ oder legen Sie sie global auf der Seite fest, indem Sie die folgende Einstellung verwenden:
<%@ Page EnableViewState="false" %>
Wenn Sie die Seite nicht zurücksenden oder die Steuerelemente auf der Seite bei jeder Anfrage immer neu generieren, sollten Sie den Ansichtsstatus auf Seitenebene deaktivieren.
Ich habe Ihnen einige Tipps gegeben, die ich beim Schreiben leistungsstarker ASP.NET-Anwendungen hilfreich finde. Wie ich bereits in diesem Artikel erwähnt habe, handelt es sich hierbei um einen vorläufigen Leitfaden und nicht um das letzte Wort zur ASP.NET-Leistung. (Informationen zum Verbessern der ASP.NET-Anwendungsleistung finden Sie unter Verbessern der ASP.NET-Leistung.) Der beste Weg zur Lösung eines bestimmten Leistungsproblems kann nur durch Ihre eigene Erfahrung gefunden werden. Diese Tipps sollen Ihnen jedoch eine gute Orientierung auf Ihrer Reise geben. In der Softwareentwicklung gibt es nur wenige Absolutheiten; jede Anwendung ist einzigartig.