Autor: Rob Howard Übersetzung: Tropische Fische
Dieser Artikel behandelt:
· Allgemeine ASP.NET-Leistungsgeheimnisse
· Nützliche Tipps und Tricks zur Verbesserung der ASP.NET-Leistung
· Empfehlungen für die Verwendung von Datenbanken in ASP.NET
· Caching und Hintergrundverarbeitung in ASP.NET
Das Schreiben einer Webanwendung mit ASP.NET ist unglaublich einfach. Es ist so einfach, dass sich viele Entwickler nicht die Zeit nehmen, ihre Anwendungen so zu entwickeln, dass sie eine gute Leistung erbringen. In diesem Artikel empfehle ich 10 Tipps zum Schreiben leistungsstarker Webanwendungen. Ich werde meine Diskussion nicht auf ASP.NET-Anwendungen beschränken, da ASP.NET-Anwendungen nur eine Teilmenge von Webanwendungen sind. Dieser Artikel soll kein definitiver Leitfaden zur Optimierung der Leistung von Webanwendungen sein – ein vollständiges Buch könnte dies problemlos tun. Stattdessen sollten wir diesen Artikel als guten Ausgangspunkt betrachten.
Bevor ich ein Workaholic wurde, bin ich viel Klettern gegangen. Bevor ich mit dem Klettern beginne, schaue ich mir die Routen am liebsten in einem Reiseführer an und lese Empfehlungen von Leuten, die auf dem Gipfel waren. Aber egal wie gut ein Reiseführer geschrieben ist, Sie benötigen tatsächliche Klettererfahrung, bevor Sie ein anspruchsvolles Ziel in Angriff nehmen. 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 Foundation Program Manager im ASP.NET-Team bei Microsoft, der Pflege und Verwaltung von www.asp.net und der Unterstützung bei der Entwicklung von Community Server, einer von mehreren bekannten ASP.NET-Anwendungen (ASP.NET-Foren). , .Text und nGallery, verbunden mit einer Plattform). Ich bin sicher, dass einige dieser Tipps, die mir geholfen haben, auch für Sie nützlich sein werden.
Sie sollten darüber nachdenken, Ihre Anwendung in mehrere logische Schichten zu unterteilen. Sie haben vielleicht schon von einer 3-Tier- (oder N-Tier-)Architektur gehört. Hierbei handelt es sich in der Regel um vorgeschriebene Strukturmuster, die das Geschäft und/oder die Hardware physisch in Funktionsbereiche unterteilen. Wenn das System einen größeren Umfang erfordert, kann problemlos mehr Hardware hinzugefügt werden. Dies führt jedoch zu Leistungseinbußen im Zusammenhang mit Geschäfts- und Maschinensprüngen, weshalb wir dies vermeiden sollten. Versuchen Sie daher nach Möglichkeit, die ASP.NET-Seite und die zugehörigen Komponenten der Seite in derselben Anwendung auszuführen.
Aufgrund der Codetrennung und der Grenzen zwischen den Ebenen kann die Verwendung von Webdiensten oder Remoting die Leistung um 20 % oder mehr reduzieren.
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, daher sollte die Leistung in der Datenschicht Ihre erste Überlegung sein, wenn Sie Ihren Code optimieren.
Bevor Sie in die Behebung der Leistungsprobleme Ihrer Anwendung investieren, sollten Sie unbedingt Ihre Anwendung analysieren, um die Grundursache des Problems zu ermitteln. Wichtige Leistungsindikatoren (z. B. derjenige, der den Prozentsatz der für die Garbage Collection aufgewendeten Zeit angibt) sind ebenfalls sehr nützlich, um herauszufinden, wo die Anwendung die meiste Zeit verbringt. Allerdings sind die Orte, an denen man Zeit verbringt, oft weniger intuitiv.
In diesem Artikel bespreche ich zwei Möglichkeiten zur Leistungsverbesserung: groß angelegte Optimierungen, z. B. die Verwendung von ASP.NET-Caching, und klein angelegte Optimierungen, die häufig wiederholt werden. Diese kleinen Optimierungsschritte sind manchmal die interessantesten. Eine kleine Änderung, die Sie an Ihrem Code vornehmen, wird tausende Male aufgerufen. Optimieren Sie große Teile, und Sie werden möglicherweise einen großen Sprung in der Gesamtleistung feststellen. Durch die Optimierung in kleinen Abschnitten können Sie bei einer bestimmten Anfrage möglicherweise einige Mikrosekunden einsparen, aber kumulativ über alle Anfragen pro Tag hinweg erzielen Sie eine unerwartete Leistungsverbesserung.
Leistung in der Datenschicht
Wenn Sie mit der Optimierung der Leistung einer Anwendung beginnen, gibt es einen entscheidenden Test, den Sie priorisieren können: Muss der Code auf eine Datenbank zugreifen? Wenn ja, wie oft kommen Sie vorbei? Beachten Sie, dass dieser Test auch auf Code angewendet werden kann, der Webdienste oder Fernsteuerung verwendet, ich werde diese jedoch in diesem Artikel nicht behandeln.
Wenn in einem bestimmten Codepfad in Ihrem Code eine Datenbankanforderung erforderlich ist und Sie andere Stellen finden, an denen Sie Optimierungen priorisieren möchten, z. B. String-Manipulation, stoppen Sie und führen Sie zuerst die kritischen Tests durch. Sofern Sie nicht mit einem wirklich schwerwiegenden Leistungsproblem zu kämpfen haben, sollten Sie Ihre Zeit besser damit verbringen, die Zeit zu optimieren, die zum Herstellen einer Verbindung zur Datenbank benötigt wird, die Menge der zurückgegebenen Daten und die Vorgänge, die Sie in die und aus der Datenbank ausführen.
Nachdem ich nun die allgemeinen Informationen behandelt habe, werfen wir einen Blick auf 10 Tipps, die Ihnen dabei helfen, die Leistung Ihrer Anwendung zu verbessern. Ich beginne mit denen, die den offensichtlichsten Einfluss auf die Leistungsverbesserung haben.
Tipp 1 – Mehrere Ergebnismengen zurückgeben
Sehen Sie sich Ihren Datenbankcode an, um zu sehen, ob Sie Anforderungspfade haben, die mehr als einmal auf die Datenbank zugreifen. Jeder dieser Roundtrips verringert die Anzahl der Anfragen, die Ihre Anwendung pro Sekunde verarbeiten kann. Durch die Rückgabe mehrerer Ergebnismengen in einer einzigen Datenbankanforderung können Sie den Gesamtzeitaufwand für die Datenbankkommunikation reduzieren. Indem Sie die Anzahl der Anfragen reduzieren, die Ihr Datenbankserver verwalten muss, machen Sie Ihr System auch skalierbarer.
Im Allgemeinen können Sie dynamische SQL-Anweisungen verwenden, um mehrere Ergebnismengen zurückzugeben. Ich bevorzuge die Verwendung gespeicherter Prozeduren. Es ist fraglich, ob Sie Geschäftslogik in eine gespeicherte Prozedur integrieren sollten, aber ich denke, wenn die Logik in einer gespeicherten Prozedur die zurückgegebenen Daten begrenzen kann (die Größe des Datensatzes verringert, die für die Netzwerkverbindung aufgewendete Zeit verringert und die Notwendigkeit beseitigt). Filtern der Logikschichtdaten), dann ist das eine gute Sache.
Mithilfe einer SqlCommand-Instanz und ihrer ExecuteReader-Methode zum Generieren einer stark typisierten Geschäftsklasse können Sie den Ergebnismengenzeiger durch den Aufruf von NextResult nach vorne verschieben. Abbildung 1 zeigt eine Beispielsitzung, die eine definierte Klasse verwendet, um mehrere ArrayLists zu generieren. Wenn Sie nur die Daten aus der Datenbank zurückgeben, die Sie benötigen, werden die Speicheranforderungen auf Ihrem Server erheblich reduziert.
1// liest die erste Ergebnismenge
2reader = command.ExecuteReader();
3
4// liest die Daten aus dieser Ergebnismenge
5while (reader.Read()) {
6 Lieferanten.Add(PopulateSupplierFromIDataReader(reader));
7}
8
9// Lesen Sie die nächste Ergebnismenge
10reader.NextResult();
11
12// lese die Daten aus diesem zweiten Ergebnissatz
13while (reader.Read()) {
14 Produkte.Add(PopulateProductFromIDataReader(reader));
15}
16
17
Tipp 2 – Paginierter Datenzugriff
Das DataGrid von ASP.NET bietet eine großartige Funktion: Unterstützung für Daten-Paging. Wenn die Paginierung im DataGrid eingerichtet ist, wird eine bestimmte Anzahl von Ergebnissen gleichzeitig angezeigt. Darüber hinaus wird unten im DataGrid eine Paging-Benutzeroberfläche zum Navigieren zwischen den Ergebnissen angezeigt. Mit der paginierten Benutzeroberfläche können Sie vorwärts oder rückwärts durch die angezeigten Daten navigieren und dabei eine bestimmte Anzahl von Ergebnissen pro Seite anzeigen.
Aber es gibt ein kleines Problem. Wenn Sie DataGrid zum Paging verwenden, müssen alle Daten an die Tabelle gebunden sein. Beispielsweise muss Ihre Datenschicht alle Daten zurückgeben, und dann muss das DataGrid alle Datensätze füllen, die basierend auf der aktuellen Seite angezeigt werden sollen. Wenn bei Verwendung der DataGrid-Paginierung 100.000 Datensätze zurückgegeben werden, werden 99.975 Datensätze pro Anfrage verworfen (vorausgesetzt, die Kapazität jeder Seite beträgt 25 Datensätze). Wenn die Anzahl der Datensätze zunimmt, wird die Leistung der Anwendung stark beeinträchtigt, da mit jeder Anfrage immer mehr Daten zurückgegeben werden müssen.
Eine Möglichkeit, besseren Paginierungscode zu schreiben, ist die Verwendung gespeicherter Prozeduren. Abbildung 2 zeigt eine gespeicherte Beispielprozedur, die die Datentabelle „Orders“ in der Nothwind-Datenbank blättert. Im Grunde müssen Sie hier lediglich den Index der Seite und die Kapazität der Seite übergeben. Die Datenbank berechnet die entsprechenden Ergebnismengen und gibt sie zurück.
1VERFAHREN ERSTELLEN nordwind_OrdersPaged
2(
3 @PageIndex int,
4 @PageSize int
5)
6AS
7BEGINNEN
8DECLARE @PageLowerBound int
9DECLARE @PageUpperBound int
10DECLARE @RowsToReturn int
11
12-- Stellen Sie zunächst die Zeilenanzahl ein
13SET @RowsToReturn = @PageSize * (@PageIndex + 1)
14SET ROWCOUNT @RowsToReturn
15
16 – Legen Sie die Seitengrenzen fest
17SET @PageLowerBound = @PageSize * @PageIndex
18SET @PageUpperBound = @PageLowerBound + @PageSize + 1
19
20-- Erstellen Sie eine temporäre Tabelle, um die ausgewählten Ergebnisse zu speichern
21TABELLE #PageIndex ERSTELLEN
zweiundzwanzig(
23 IndexId int IDENTITY (1, 1) NICHT NULL,
24 OrderID int
25)
26
27 – In die temporäre Tabelle einfügen
28INSERT INTO #PageIndex (OrderID)
29AUSWÄHLEN
30 Bestell-ID
31VON
32 Bestellungen
33ORDER BY
34 Bestell-ID-Beschreibung
35
36 – Gesamtzahl zurückgeben
37SELECT COUNT(OrderID) FROM Orders
38
39-- Seitenergebnisse zurückgeben
40AUSWÄHLEN
41 O.*
42VON
43 Befehle O,
44 #PageIndex SeitenIndex
45WO
46 O.OrderID = PageIndex.OrderID AND
47 PageIndex.IndexID > @PageLowerBound AND
48 PageIndex.IndexID < @PageUpperBound
49ORDER BY
50 SeitenIndex.IndexID
51
52ENDE
53
54
Während des Zivildienstzeitraums haben wir eine Paging-Server-Steuerung geschrieben, um diese Daten-Paging durchzuführen. Sie werden feststellen, dass ich die in Tipp 1 besprochene Idee verwendet habe, 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 durchgeführter Anfrage variieren. Beispielsweise kann eine WHERE-Klausel verwendet werden, um die zurückgegebenen Daten einzuschränken. Wir müssen die Gesamtzahl der zurückzugebenden Datensätze kennen, um die Gesamtzahl der Seiten zu berechnen, die in der paginierten Benutzeroberfläche angezeigt werden sollen. Wenn es beispielsweise insgesamt 1.000.000 Datensätze gibt und eine WHERE-Klausel verwendet wird, um diese Datensätze auf 1.000 Datensätze zu filtern, muss die Paging-Logik die Gesamtzahl der Datensätze kennen, um die Paging-Benutzeroberfläche entsprechend zu übermitteln.
Tipp 3 – Verbindungspooling
Das Herstellen einer TCP-Verbindung zwischen Ihrer Webanwendung und SQL Server kann ein kostspieliger Vorgang sein. Entwickler bei Microsoft nutzen bereits seit einiger Zeit Verbindungspooling, um Verbindungen zu Datenbanken wiederzuverwenden. Anstatt für jede Anfrage eine neue TCP-Verbindung herzustellen, wird eine neue Verbindung nur dann hergestellt, wenn keine verfügbare Verbindung im Verbindungspool vorhanden ist. Wenn die Verbindung geschlossen wird, wird sie an den Verbindungspool zurückgegeben. Die Verbindung zur Datenbank wird weiterhin aufrechterhalten, anstatt die TCP-Verbindung vollständig zu zerstören.
Natürlich müssen Sie auf undichte Verbindungen achten. Schließen Sie immer Ihre Verbindungen, wenn Sie sie nicht mehr verwenden. Ich wiederhole: Egal, was jemand über den Garbage-Collection-Mechanismus von Microsoft .NET Framework sagt, Sie müssen immer explizit die Close- oder Dispose-Methode für Ihre Verbindung aufrufen, wenn Sie damit fertig sind. Vertrauen Sie nicht darauf, dass die Common Language Runtime (CLR) Ihre Verbindung zu einem festgelegten Zeitpunkt für Sie bereinigt und schließt. Die CLR zerstört schließlich die Klasse und erzwingt das Schließen der Verbindung. Es gibt jedoch keine Garantie dafür, wann der Garbage-Collection-Mechanismus für das Objekt tatsächlich ausgeführt wird.
Um mit Verbindungspooling die besten Ergebnisse zu erzielen, müssen Sie einige Regeln befolgen. Öffnen Sie zunächst eine Verbindung, erledigen Sie die Arbeit und schließen Sie dann die Verbindung. Es ist in Ordnung, wenn Sie (vorzugsweise unter Anwendung von Tipp 1) die Verbindung pro Anfrage mehrmals öffnen und schließen müssen. Dies ist viel besser, als die Verbindung offen zu lassen und sie an mehrere verschiedene Methoden zu übergeben. Zweitens verwenden Sie dieselbe Verbindungszeichenfolge (und natürlich dieselbe Thread-ID, wenn Sie die integrierte Authentifizierung verwenden). Wenn Sie nicht dieselbe Verbindungszeichenfolge verwenden, beispielsweise unterschiedliche benutzerdefinierte Verbindungszeichenfolgen basierend auf dem angemeldeten Benutzer, erhalten Sie nicht denselben optimalen Wert, den der Verbindungspool bereitstellt. Und wenn Sie die integrierte Authentifizierung verwenden, wenn Sie sich als eine große Anzahl von Benutzern ausgeben, ist Ihr Verbindungspool ebenfalls deutlich weniger effizient. Die .NET CLR-Datenleistungsindikatoren können hilfreich sein, wenn Sie Leistungsprobleme im Zusammenhang mit dem Verbindungspooling aufspüren möchten.
Wenn Ihre Anwendung eine Verbindung zu einer Ressource wie einer Datenbank herstellt oder in einem anderen Prozess ausgeführt wird, sollten Sie sich dabei auf die Zeit konzentrieren, die zum Herstellen einer Verbindung mit der Ressource benötigt wird, auf die Zeit, die zum Senden und Empfangen von Daten benötigt wird, und auf die Zeit, die zum Herstellen einer Verbindung mit der Ressource benötigt wird Das Senden und Empfangen von Daten erfordert eine Reihe von Roundtrips zur Datenbank, die optimiert werden müssen. Die Optimierung jeglicher Art von Prozesssprüngen in Ihrer Anwendung ist der erste Schritt, um eine bessere Leistung zu erzielen.
Die Anwendungsschicht enthält die Logik, die eine Verbindung zu Ihrer Datenschicht herstellt und die Daten in sinnvolle Klasseninstanzen und logische Prozesse umwandelt. Auf einem Community-Server generieren Sie beispielsweise ein Forum oder eine Thread-Sammlung und wenden Geschäftsregeln wie Berechtigungen an. Noch wichtiger ist, dass hier die Caching-Logik ausgeführt wird.
Tipp 4 – ASP.NET-Puffer-API
Bevor Sie mit dem Schreiben der ersten Codezeile in Ihrer Anwendung beginnen, sollten Sie zunächst darüber nachdenken, die Anwendungsschicht so zu gestalten, dass sie die Caching-Funktionen von ASP.NET maximiert und nutzt.
Wenn Ihre Komponente in einer ASP.NET-Anwendung ausgeführt wird, verweisen Sie einfach in Ihrem Anwendungsprojekt auf System.Web.dll. Wenn Sie auf den Cache zugreifen müssen, verwenden Sie die Eigenschaft HttpRuntime.Cache (auf dieses Objekt kann auch über Page.Cache und HttpContext.Cache zugegriffen werden).
Es gibt verschiedene Richtlinien für die Verwendung zwischengespeicherter Daten. Erstens: Wenn die Daten mehrfach verwendet werden können, ist die Zwischenspeicherung eine gute Wahl. Zweitens: Wenn die Daten allgemeiner Natur sind und sich nicht auf eine bestimmte Anfrage oder einen bestimmten Benutzer beziehen, ist die Zwischenspeicherung eine gute Option. Wenn die Daten benutzer- oder anforderungsspezifisch sind, aber eine lange Lebensdauer haben, können sie zwischengespeichert, aber möglicherweise nicht häufig verwendet werden. Drittens besteht ein oft übersehener Grundsatz darin, dass man manchmal zu viel zwischenspeichern kann. Normalerweise sollten Sie auf einem x86-Computer einen Prozess ausführen, der nicht mehr als 800 MB private Bytes verwendet, um die Wahrscheinlichkeit von Fehlern aufgrund von unzureichendem Arbeitsspeicher zu verringern. Daher sollte das Caching begrenzt werden. Mit anderen Worten: Möglicherweise müssen Sie das Ergebnis einer Berechnung wiederverwenden. Wenn diese Berechnung jedoch zehn Parameter erfordert, müssen Sie möglicherweise versuchen, zehn Permutationen zwischenzuspeichern, was zu Problemen führen kann. Fehler wegen unzureichendem Arbeitsspeicher aufgrund von Übercaching treten in ASP.NET am häufigsten auf, 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 Leeren des Caches zu erzwingen, indem nicht verwendete Elemente automatisch aus dem Cache entfernt werden, 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“ in der Juli-Ausgabe 2004 des MSDN Magazine. Um die Architektur des Caches zu verstehen, sehen Sie sich Abbildung 3 an.
Tipp 5 – Caching pro Anfrage
Zu Beginn dieses Artikels habe ich erwähnt, dass kleine Verbesserungen an häufig durchlaufenen Codepfaden zu großen Gesamtleistungssteigerungen führen können. Von diesen kleinen Verbesserungen gefällt mir definitiv eine am besten und ich nenne sie Caching pro Anfrage.
Caching-APIs sind darauf ausgelegt, Daten für einen längeren Zeitraum oder bis bestimmte Bedingungen erfüllt sind, zwischenzuspeichern. Beim Caching pro Anfrage werden Daten jedoch nur für die Dauer dieser Anfrage zwischengespeichert. Für jede Anfrage wird häufig auf einen bestimmten Codepfad zugegriffen, die Daten werden jedoch nur einmal extrahiert, angewendet, geändert oder aktualisiert. Das klingt etwas theoretisch, also geben wir ein konkretes Beispiel.
In einer Community-Server-Forumsanwendung benötigt jedes auf der Seite verwendete Serversteuerelement Personalisierungsdaten, um zu bestimmen, welches Erscheinungsbild verwendet werden soll, welche Stiltabelle verwendet werden soll, sowie andere Personalisierungsdaten. Einige dieser Daten können langfristig zwischengespeichert werden, andere werden jedoch nur einmal pro Anfrage abgerufen und dann während dieser Anfrage mehrmals wiederverwendet, beispielsweise für die Darstellung eines Steuerelements.
Um die Zwischenspeicherung pro Anfrage zu erreichen, verwenden Sie ASP.NET HttpContext. Für jede Anfrage wird eine HttpContext-Instanz erstellt, auf die während der Anfrage von überall innerhalb der HttpContext.Current-Eigenschaft zugegriffen werden kann. Die HttpContext-Klasse verfügt über eine spezielle Items-Sammlungseigenschaft; Objekte und Daten, die dieser Items-Sammlung hinzugefügt werden, werden nur für die Dauer der Anforderung zwischengespeichert. So wie Sie Caching zum Speichern häufig aufgerufener Daten verwenden können, können Sie HttpContext.Items auch zum Speichern von Daten verwenden, die nur auf Anforderungsbasis verwendet werden. Die Logik dahinter ist sehr einfach: Daten werden der HttpContext.Items-Sammlung hinzugefügt, wenn sie nicht vorhanden sind, und bei nachfolgenden Suchvorgängen werden nur die Daten in HttpContext.Items zurückgegeben.
Tipp 6 – Hintergrundverarbeitung
Der Weg zum Code sollte so schnell wie möglich sein, oder? Es kann vorkommen, dass Sie feststellen, dass Sie eine sehr ressourcenintensive Aufgabe ausführen, die bei jeder oder allen n Anfragen ausgeführt wird. 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 der Codepfad zum Veröffentlichen neuer Beiträge extrem langsam war. Jedes Mal, wenn ein neuer Beitrag veröffentlicht 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, die Charakter-Emoticons des Beitrags analysieren, den Beitrag markieren und indizieren und die hinzufügen Fügen Sie auf Anfrage einen Beitrag zur entsprechenden Warteschlange hinzu, validieren Sie den Anhang und senden Sie schließlich unmittelbar nach der Veröffentlichung des Beitrags 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 SMTP-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 entschieden uns, Code zu verwenden, den ich als Prototyp für die Datencache-Invalidierung verwendet hatte, der schließlich in Visual Studio 2005 enthalten war.
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, stoppt diese Timer-Instanz das Auslösen von Ereignissen. Da die CLR außerdem einen harten Standard für die Anzahl der Threads pro Prozess hat, kann es auf einem stark ausgelasteten Server vorkommen, dass der Timer möglicherweise nicht garantiert, dass die Threads den Vorgang weiterhin abschließen, was in gewissem Maße zu Verzögerungen führen kann . 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 leicht verständliches 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.
Indem Sie oben auf der Seite die folgende Zeile hinzufügen:
<%@ Page OutputCache VaryByParams="none" Duration="60" %>
Sie können die Ausgabe für diese Seite effektiv einmal 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 programmierbarer 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.
Vielen Menschen ist nicht bewusst, dass ASP.NET-Seiten bei Verwendung von Ausgabe-Caching auch HTTP-Header generieren, die an Caching-Server weitergegeben werden, wie sie beispielsweise von Microsoft Internet Security and Acceleration Server oder Akamai verwendet werden. Nach dem Festlegen des Headers der HTTP-Cache-Tabelle können Dokumente auf diesen Netzwerkressourcen zwischengespeichert und Client-Anfragen ohne Rückkehr zum Ursprungsserver erfüllt werden.
Daher wird die Verwendung des Seitenausgabe-Cachings Ihre Anwendung nicht effizienter machen, aber möglicherweise die Belastung des Servers verringern, da die Downstream-Caching-Technologie das Dokument zwischenspeichert. Natürlich kann es sich dabei nur um anonyme Inhalte handeln; sobald sie nachgelagert sind, werden Sie die 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 (auch nur, um den Kernel-Cache zu verwenden)
Wenn Sie IIS 6.0 (Windows Server 2003) nicht ausführen, 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 Entwicklungsmanager 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 komprimierbar sie sein können und ob der Client-Browser dies unterstützt (IIS sendet gzip-komprimierte Inhalte nur an Clients, die die Gzip-Komprimierung unterstützen, wie z. B. Internet Explorer 6.0 und Firefox), kann Ihr Server mehr bereitstellen 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, 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 bemerkenswerteste 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.
Zusammenfassung
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.
Siehe die Seitenleiste „Häufige Leistungsmythen“.
Rob Howard ist der Gründer von Telligent Systems, spezialisiert auf leistungsstarke Webanwendungen, Wissensdatenbankmanagement und Kollaborationssysteme. Rob war zuvor bei Microsoft beschäftigt, wo er beim Entwurf der Infrastruktur für ASP.NET 1.0, 1.1 und 2.0 half. Um Rob zu kontaktieren, besuchen Sie bitte [email protected] .
Ursprünglicher Link: http://msdn.microsoft.com/msdnmag/issues/05/01/ASPNETPerformance/default.aspx