Nach langem Ringen mit dem Page Lifecycle in ASP.NET und dessen Performance haben wir damit begonnen, unsere Web-App so umzugestalten, dass sie Webdienste (plein-vanilla .asmx .NET-Webdienste) und jQuery auf der Clientseite implementiert . HINWEIS: Dies implementiert MVC oder ASP.NET in keiner Weise, dies sind nur Webdienste.
In beiden Versionen der Anwendung generieren wir alle Inhalte dynamisch auf einer einzelnen Seite. In der ASP.NET-Dispensierung bedeutete dies (aufgrund des Page Lifecycle), dass die gesamte Seite mit (fast) jedem AJAX-Aufruf abgerissen und neu aufgebaut oder in das Web-Formular geändert werden musste. Dies stellte ein großes Skalierbarkeitsproblem für eine App dar, die viele gleichzeitige Benutzer bedienen sollte. In der Disposition von web services / jQuery können wir uns selektiv nur auf die Elemente des DOM konzentrieren, die Daten an den Server senden oder empfangen müssen, was weit weniger Anfragen und eine viel schnellere Benutzererfahrung bedeutet.
Die erste Iteration der App zeigte eine Leistungsverbesserung um eine Größenordnung; Da wir jedoch begonnen haben, immer mehr Webdienste zu erstellen, entspricht die Leistung der App jetzt der Leistung der ASP.NET-Verteilung.
Nach vielem Googeln / Soul-Suchen und Lasttests wurde klar, dass HTTP Session der Schuldige war. Im Wesentlichen ist jeder Lesevorgang (und manchmal, indem einfach Session in den Web-Service-Methodenbereich eingeschlossen wird) ein blockierender Aufruf, der eine Verzögerung von 500 ms einführt. Sobald Sie wissen, wo Sie suchen müssen, ist dies in der MSDN-Literatur ausführlich dokumentiert. Wie implementiert, transformiert Session (wenn sie von mehreren Webdiensten von demselben Benutzer verwendet wird) asynchrone Anforderungen in synchrone Anforderungen mit einem Puffer von 500 ms. Wir mildern dies derzeit, indem wir alle unsere AJAX-Anrufe als aufeinander folgende "Erfolgsereignisse" verketten, was sie zu synchronen Anfragen seitens des Kunden macht. Dies beseitigt die Verzögerung von 500 ms, die beim Anfordern eines Lesevorgangs bei einem gesperrten Sitzungsobjekt auftritt.
Das Verhalten der clientseitigen App auf eine "synchrone" Weise hat viele Leistungsprobleme gelöst; Es ist jedoch nur eine kurzfristige Lösung.
Welche realisierbaren (skalierbaren!) Alternativen zu Session existieren, auch wenn wir nicht daran denken, dass wir nicht auf ASP.NET oder MVC oder WCF usw. stehen? Unsere größte Hürde ist die Beständigkeit unserer Metadaten-Sammlung, die für jeden Benutzer beim Login initialisiert wird. Dies ist die teuerste Operation in der App (um den Faktor 10 oder mehr) und eine, die wir nur einmal ausführen möchten. Die Session bot eine einfache Möglichkeit, einmal zu den Oldies zu schwitzen und niemals zurückzuschauen. aber dieser Ansatz sieht weniger durchführbar aus.
Ein Ansatz könnte darin bestehen, diese einzelne Metadaten-Sammlungsgottklasse zu eliminieren und diese monotheistische Klasse in eine polytheistische Sammlung von Halbgöttern umzuwandeln. Die Halbgötter zu stempeln könnte häufiger zu geringeren Kosten durchgeführt werden. Machbar, erfordert jedoch eine erhebliche Umgestaltung, umfangreiche Entwicklung und QA-Zeit. Ein anderer Kandidat speichert einfach alle Statusinformationen in der Datenbank, aber dies hat seine eigenen Kosten - Latenz nicht zuletzt.
Gibt es andere Lösungen für dieses Problem, die einen geringeren Implementierungsaufwand erfordern?
Es ist wirklich einfach, Ihr Problem zu beheben.
Erstellen Sie eine Tabelle in SQL Server:
Erstellen Sie eine Tabellensitzung ( SessionId int (oder eine GUID) ... ... )
Erstellen Sie separate Tabellen mit einem Fremdschlüssel, der zu Ihrer Sitzungstabelle zurückkehrt (mit Löschkaskade), und speichern Sie sitzungsspezifische Informationen
Speichern Sie die SessionId auf der Anwendungsseite entweder in einem Cookie oder in der Abfragezeichenfolge. Sie können dann alle Sitzungsinformationen nach Bedarf abrufen. Dies funktioniert viel schneller als die Standard-Session-State-Provider, da Sie nur Informationen erhalten, wenn Sie die Informationen benötigen! Außerdem müssen Sie das Standardsitzungsobjekt nicht länger auf Ihrem Rücken tragen.
Wirf noch mehr Hardware drauf!
Nein, ernsthaft. Bei Websites wie Ihrem können Sie davon profitieren, einen separaten Computer als Sitzungsstatusserver zu konfigurieren (siehe MSDN-Dokumentation ), die Sie über die IIS-Benutzeroberfläche oder die IIS-Befehlszeile ausführen können.
Nachdem Sie den Statusserver eingerichtet haben, müssen Sie Ihre Webkonfiguration für die Verwendung des neuen Computers einrichten. Eine schöne Beschreibung des Prozesses ist verfügbar hier , aber kurz gesagt, setzen Sie das system.web.sessionState-Element von web.config wie folgt:
%Vor%Nun, wenn Sie wirklich schwer arbeiten wollen, können Sie viele Zustandsserver haben und Ihre eigene Routine schreiben, um herauszufinden, welcher Server Ihren Sitzungszustand enthält. Siehe eine Beschreibung des Prozesses hier . Diese Strategie sollte Ihnen helfen, mehr oder weniger unbegrenzt zu skalieren, ohne Ihre Verwendung von HttpSession aufzugeben.
Wenn die meisten Ihrer Anforderungen nur Lesezugriff auf die Sitzung erfordern, haben Sie eine einfache Lösung. Setzen Sie das EnableSessionState
-Attribut auf ReadOnly und sie erhalten nur eine schreibgeschützte Sperre für die Sitzung. Ich bin nicht sicher, wie man das auf Webservices anwendet, aber ich bin sicher, dass es eine entsprechende Einstellung gibt.
Darüber hinaus sperrt ASP.NET das Sitzungsobjekt für die gesamte Dauer jeder Anforderung und serialisiert die Anforderungen effektiv. Eine Lösung für die parallele Ausführung von Anforderungen besteht nicht darin, die integrierten Sitzungen zu verwenden und eine eigene Lösung zu erstellen. Der Vorteil hier ist, dass Sie viel feinkörniger sperren können, so dass bestimmte Aspekte der Anfragen synchronisiert werden müssen und seriell ablaufen, aber die Mehrheit jeder Anfrage kann parallel laufen.
Eine alternative Lösung besteht darin, die Anzahl der parallelen Anrufe pro Anfrage zu reduzieren. Versuche nicht, sie parallel laufen zu lassen. Stapeln Sie sie auf der Client-Seite und senden Sie sie als eine einzige Anfrage. Sie haben weniger Overhead in der Anzahl der Anfragen, weniger Wahrscheinlichkeit, dass ein einzelner Client viele Ressourcen in Anspruch nimmt, und können höchstwahrscheinlich insgesamt und für jede aggregierte Anfrage eine bessere Leistung erzielen.
Ohne Ihren Code zu sehen oder was Sie in der Sitzung laden und speichern, ist es schwierig, Ihnen tatsächlich zu helfen. Allerdings ist eine 0,5-Sekunden-Verzögerung für die Sitzung meiner Erfahrung nach sicherlich nicht normal. Ich würde sehr gerne sehen, dass die Dokumentation, auf die Sie sich beziehen, besagt, dass dies so ist.
Gut geschrieben asp.net (ob Webforms, asmx, mvc oder andere Optionen) ist sicherlich sehr skalierbar.
Da wird gesagt, wo wird Ihre Sitzung gespeichert? In Bearbeitung? Der IIS-Statusserver? In einer DB? Wie viele Daten speichern Sie wirklich in der Sitzung? Verwenden Sie wirklich alle Daten, die Sie fast jede Anfrage in Sitzung laden?
Es ist schwierig, Ihnen eine echte Antwort zu geben, ohne Ihre Problemdomäne besser zu verstehen: wofür müssen Sie den Status halten, wie groß sind die Objekte, die Sie verwenden, um den Status zu behalten, wie viel Verkehr Sie haben, etc etc.
Tags und Links c# jquery ajax web-services state