Bewährte Methoden für die ASP.net Core LeistungASP.NET Core Performance Best Practices

Von Mike RousosBy Mike Rousos

Dieser Artikel enthält Richtlinien für bewährte Methoden für die Leistung mit ASP.net Core.This article provides guidelines for performance best practices with ASP.NET Core.

ZwischenspeichernCache aggressively

Caching wird in mehreren Teilen dieses Dokuments erläutert.Caching is discussed in several parts of this document. Weitere Informationen finden Sie unter Zwischenspeichern von Antworten in ASP.net Core.For more information, see Zwischenspeichern von Antworten in ASP.net Core.

Verstehen von Hot-CodepfadeUnderstand hot code paths

In diesem Dokument wird ein Hot-Codepfad als Codepfad definiert, der häufig aufgerufen wird und in dem ein Großteil der Ausführungszeit auftritt.In this document, a hot code path is defined as a code path that is frequently called and where much of the execution time occurs. Hot-Codepfade schränken die horizontale Skalierung und Leistung der APP ein und werden in verschiedenen Teilen dieses Dokuments erläutert.Hot code paths typically limit app scale-out and performance and are discussed in several parts of this document.

Vermeiden von blockierenden aufrufenAvoid blocking calls

ASP.net Core-apps sollten so entworfen werden, dass viele Anforderungen gleichzeitig verarbeitet werden.ASP.NET Core apps should be designed to process many requests simultaneously. Asynchrone APIs ermöglichen es einem kleinen Thread Pool, Tausende gleichzeitiger Anforderungen zu verarbeiten, indem nicht auf blockierende Aufrufe gewartet wird.Asynchronous APIs allow a small pool of threads to handle thousands of concurrent requests by not waiting on blocking calls. Anstatt auf den Abschluss einer synchronen Aufgabe mit langer Laufzeit zu warten, kann der Thread an einer anderen Anforderung arbeiten.Rather than waiting on a long-running synchronous task to complete, the thread can work on another request.

Ein häufiges Leistungsproblem bei ASP.net Core-Apps ist das Blockieren von aufrufen, die asynchron sein könnten.A common performance problem in ASP.NET Core apps is blocking calls that could be asynchronous. Viele synchrone blockierende Aufrufe führen zu einem Thread Pool-Hunger und zu beeinträchtigten Reaktionszeiten.Many synchronous blocking calls lead to Thread Pool starvation and degraded response times.

Nicht :Do not :

  • Blockieren Sie die asynchrone Ausführung durch Aufrufen von " Task. Wait " oder " Task. Result".Block asynchronous execution by calling Task.Wait or Task.Result.
  • Abrufen von Sperren in gemeinsamen Codepfade.Acquire locks in common code paths. ASP.net Core-apps sind am leistungsfähigsten, wenn Sie entwickelt werden, um Code parallel auszuführen.ASP.NET Core apps are most performant when architected to run code in parallel.
  • " Task. Run " aufzurufen und sofort darauf warten.Call Task.Run and immediately await it. ASP.net Core bereits app-Code in normalen Thread Pool-Threads ausführt, führt das Aufrufen von "Task. Run" nur zu einer zusätzlichen unnötigen Thread Pool-Zeitplanung.ASP.NET Core already runs app code on normal Thread Pool threads, so calling Task.Run only results in extra unnecessary Thread Pool scheduling. Auch wenn der geplante Code einen Thread blockieren würde, wird dies von Task. Run nicht verhindert.Even if the scheduled code would block a thread, Task.Run does not prevent that.

Do Ausführen:Do :

  • Asynchrone Hot-Codepfade .Make hot code paths asynchronous.
  • Aufrufen von Datenzugriff, e/a und langfristigen Operations-APIs, wenn eine asynchrone API verfügbar ist.Call data access, I/O, and long-running operations APIs asynchronously if an asynchronous API is available. Verwenden Sie " Task. Run " nicht , um eine synchrone API asynchron zu machen.Do not use Task.Run to make a synchronous API asynchronous.
  • Aktionen für Controller/ Razor Seite asynchron durchführen.Make controller/Razor Page actions asynchronous. Die gesamte-aufrufsstapel ist asynchron, um von Async/ Erwartungs Mustern zu profitieren.The entire call stack is asynchronous in order to benefit from async/await patterns.

Ein Profiler, z. b. perfview, kann verwendet werden, um häufig dem Thread Poolhinzugefügte Threads zu suchen.A profiler, such as PerfView, can be used to find threads frequently added to the Thread Pool. Das Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start Ereignis gibt einen Thread an, der dem Thread Pool hinzugefügt wurde.The Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start event indicates a thread added to the thread pool.

IEnumerable <T> oder iasyncenumerable zurückgeben<T>Return IEnumerable<T> or IAsyncEnumerable<T>

IEnumerable<T>Die Rückgabe von einer Aktion führt zu einer synchronen Auflistungs Iterationen durch das Serialisierungsprogramm.Returning IEnumerable<T> from an action results in synchronous collection iteration by the serializer. Das Ergebnis sind die Blockierung von Aufrufen und die potenzielle Außerkraftsetzung des Threadpools.The result is the blocking of calls and a potential for thread pool starvation. Um eine synchrone Enumeration zu vermeiden, verwenden Sie ToListAsync vor dem Zurückgeben der Enumerable.To avoid synchronous enumeration, use ToListAsync before returning the enumerable.

Ab ASP.net Core 3,0 IAsyncEnumerable<T> kann als Alternative zu verwendet werden, IEnumerable<T> die asynchron auflistet.Beginning with ASP.NET Core 3.0, IAsyncEnumerable<T> can be used as an alternative to IEnumerable<T> that enumerates asynchronously. Weitere Informationen finden Sie unter Rückgabe Typen von Controller Aktionen.For more information, see Controller action return types.

Minimieren von großen Objekt ZuordnungenMinimize large object allocations

Der .net Core-Garbage Collector verwaltet die Zuweisung und Freigabe von Arbeitsspeicher automatisch in ASP.net Core apps.The .NET Core garbage collector manages allocation and release of memory automatically in ASP.NET Core apps. Das automatische Garbage Collection bedeutet im Allgemeinen, dass Entwickler sich nicht darum kümmern müssen, wie oder wann Speicher freigegeben wird.Automatic garbage collection generally means that developers don't need to worry about how or when memory is freed. Das Bereinigen von Objekten, auf die nicht verwiesen wird, benötigt jedoch CPU-Zeit, sodass Entwickler die Zuweisung von Objekten in Hot-CodepfadeHowever, cleaning up unreferenced objects takes CPU time, so developers should minimize allocating objects in hot code paths. Die Garbage Collection ist für große Objekte besonders aufwendig (> 85 KB).Garbage collection is especially expensive on large objects (> 85 K bytes). Große Objekte werden auf dem großen Objekt Heap gespeichert und erfordern eine vollständige (Generation 2) Garbage Collection zum Bereinigen.Large objects are stored on the large object heap and require a full (generation 2) garbage collection to clean up. Im Gegensatz zu den Sammlungen der Generation 0 und der Generation 1 erfordert eine Sammlung der Generation 2 eine vorübergehende Unterbrechung der APPUnlike generation 0 and generation 1 collections, a generation 2 collection requires a temporary suspension of app execution. Häufige Zuordnungen und Aufhebung der Zuordnung von großen Objekten können zu inkonsistenter Leistung führen.Frequent allocation and de-allocation of large objects can cause inconsistent performance.

Empfehlungen:Recommendations:

  • Sollten Sie große Objekte zwischenspeichern, die häufig verwendet werden.Do consider caching large objects that are frequently used. Das Zwischenspeichern von großen Objekten verhindert teure Zuweisungen.Caching large objects prevents expensive allocations.
  • Verwenden Sie einen arraypool <T> , um große Arrays zu speichern.Do pool buffers by using an ArrayPool<T> to store large arrays.
  • Weisen Sie nicht viele kurzlebige große Objekte in den Pfaden für heiße Codeszu.Do not allocate many, short-lived large objects on hot code paths.

Arbeitsspeicher Probleme, wie z. b. die vorstehenden, können diagnostiziert werden, indem Garbage Collection (GC)-Statistiken in perfview überprüft und Folgendes überprüft wird:Memory issues, such as the preceding, can be diagnosed by reviewing garbage collection (GC) stats in PerfView and examining:

  • Pausenzeit für Garbage Collection.Garbage collection pause time.
  • Der Prozentsatz der Prozessorzeit, der in Garbage Collection aufgewendet wird.What percentage of the processor time is spent in garbage collection.
  • Wie viele Garbage Collections sind die Generation 0, 1 und 2.How many garbage collections are generation 0, 1, and 2.

Weitere Informationen finden Sie unter Garbage Collection und Leistung.For more information, see Garbage Collection and Performance.

Optimieren des Datenzugriffs und der e/a-VorgängeOptimize data access and I/O

Interaktionen mit einem Datenspeicher und anderen Remote Diensten sind häufig die langsamsten Teile einer ASP.net Core-app.Interactions with a data store and other remote services are often the slowest parts of an ASP.NET Core app. Das effiziente lesen und Schreiben von Daten ist wichtig für eine gute Leistung.Reading and writing data efficiently is critical for good performance.

Empfehlungen:Recommendations:

  • Ruft alle Datenzugriffs-APIs asynchron auf.Do call all data access APIs asynchronously.
  • Rufen Sie nicht mehr Daten ab, als erforderlich sind.Do not retrieve more data than is necessary. Schreiben Sie Abfragen, um nur die Daten zurückzugeben, die für die aktuelle HTTP-Anforderung erforderlich sind.Write queries to return just the data that's necessary for the current HTTP request.
  • In Erwägung gezogen , dass Daten, auf die häufig zugegriffen wird, von einer Datenbank oder einem Remote Dienst abgerufen werden, wenn etwas veraltete Daten zulässig sind.Do consider caching frequently accessed data retrieved from a database or remote service if slightly out-of-date data is acceptable. Verwenden Sie je nach Szenario einen MemoryCache oder einen distributedcache.Depending on the scenario, use a MemoryCache or a DistributedCache. Weitere Informationen finden Sie unter Zwischenspeichern von Antworten in ASP.net Core.For more information, see Zwischenspeichern von Antworten in ASP.net Core.
  • Minimieren Sie die Netzwerkroundtrips.Do minimize network round trips. Das Ziel besteht darin, die erforderlichen Daten in einem einzigen Aufruf anstelle mehrerer Aufrufe abzurufen.The goal is to retrieve the required data in a single call rather than several calls.
  • Verwenden Sie keine nach Verfolgungs Abfragen in Entity Framework Core, wenn Sie schreibgeschützte Zwecke auf Daten zugreifen.Do use no-tracking queries in Entity Framework Core when accessing data for read-only purposes. EF Core können die Ergebnisse von Abfragen ohne Nachverfolgung effizienter zurückgeben.EF Core can return the results of no-tracking queries more efficiently.
  • Filtern und Aggregieren Sie LINQ-Abfragen (z. b. mit .Where .Select .Sum den Anweisungen, oder), damit die Filterung von der Datenbank ausgeführt wird.Do filter and aggregate LINQ queries (with .Where, .Select, or .Sum statements, for example) so that the filtering is performed by the database.
  • Beachten Sie , dass EF Core einige Abfrage Operatoren auf dem Client auflöst, was zu einer ineffizienten Abfrage Ausführung führen kann.Do consider that EF Core resolves some query operators on the client, which may lead to inefficient query execution. Weitere Informationen finden Sie unter Leistungsprobleme bei der Client Evaluierung.For more information, see Client evaluation performance issues.
  • Verwenden Sie keine Projektions Abfragen für Auflistungen, die zum Ausführen von "N + 1"-SQL-Abfragen führen können.Do not use projection queries on collections, which can result in executing "N + 1" SQL queries. Weitere Informationen finden Sie unter Optimierung korrelierter Unterabfragen.For more information, see Optimization of correlated subqueries.

Unter EF High Performance finden Sie Ansätze, die die Leistung in hochskalierbaren apps verbessern können:See EF High Performance for approaches that may improve performance in high-scale apps:

Es wird empfohlen, die Auswirkungen der vorangehenden Hochleistungs Ansätze zu messen, bevor Sie einen Commit für die Codebasis ausführen.We recommend measuring the impact of the preceding high-performance approaches before committing the code base. Die zusätzliche Komplexität kompilierter Abfragen kann die Leistungsverbesserung nicht rechtfertigen.The additional complexity of compiled queries may not justify the performance improvement.

Abfrage Probleme können erkannt werden, indem die Zeit für den Zugriff auf Daten mit Application Insights oder mit Profil Erstellungs Tools überprüft wird.Query issues can be detected by reviewing the time spent accessing data with Application Insights or with profiling tools. Die meisten Datenbanken stellen auch Statistiken für häufig ausgeführte Abfragen zur Verfügung.Most databases also make statistics available concerning frequently executed queries.

Pool-http-Verbindungen mit httpclientfactoryPool HTTP connections with HttpClientFactory

Obwohl HttpClient die IDisposable -Schnittstelle implementiert, ist Sie für die Wiederverwendung vorgesehen.Although HttpClient implements the IDisposable interface, it's designed for reuse. Geschlossene HttpClient Instanzen lassen für kurze Zeit Sockets im TIME_WAIT Zustand geöffnet.Closed HttpClient instances leave sockets open in the TIME_WAIT state for a short period of time. Wenn ein Codepfad, der Objekte erstellt und freigibt HttpClient , häufig verwendet wird, kann die APP verfügbare Sockets ausschöpfen.If a code path that creates and disposes of HttpClient objects is frequently used, the app may exhaust available sockets. Httpclientfactory wurde in ASP.net Core 2,1 als Lösung für dieses Problem eingeführt.HttpClientFactory was introduced in ASP.NET Core 2.1 as a solution to this problem. Er verarbeitet das Pooling von http-Verbindungen, um Leistung und Zuverlässigkeit zu optimieren.It handles pooling HTTP connections to optimize performance and reliability.

Empfehlungen:Recommendations:

Häufige Codepfade schnell haltenKeep common code paths fast

Sie möchten, dass der gesamte Code schnell ist.You want all of your code to be fast. Häufig aufgerufene Codepfade sind die wichtigsten, die optimiert werden können.Frequently-called code paths are the most critical to optimize. Dazu gehören:These include:

  • Middlewarekomponenten in der Anforderungs Verarbeitungs Pipeline der APP, insbesondere Middleware, die früh in der Pipeline ausgeführt wird.Middleware components in the app's request processing pipeline, especially middleware run early in the pipeline. Diese Komponenten haben große Auswirkungen auf die Leistung.These components have a large impact on performance.
  • Code, der für jede Anforderung oder mehrmals pro Anforderung ausgeführt wird.Code that's executed for every request or multiple times per request. Beispielsweise benutzerdefinierte Protokollierung, Autorisierungs Handler oder Initialisierung vorübergehender Dienste.For example, custom logging, authorization handlers, or initialization of transient services.

Empfehlungen:Recommendations:

Ausführen von Aufgaben mit langer Ausführungszeit außerhalb von HTTP-AnforderungenComplete long-running Tasks outside of HTTP requests

Die meisten Anforderungen an eine ASP.net Core-App können von einem Controller oder Seiten Modell verarbeitet werden, das die erforderlichen Dienste aufruft und eine HTTP-Antwort zurückgibt.Most requests to an ASP.NET Core app can be handled by a controller or page model calling necessary services and returning an HTTP response. Bei einigen Anforderungen, die Aufgaben mit langer Ausführungszeit betreffen, ist es besser, den gesamten Anforderungs Antwortprozess asynchron auszuführen.For some requests that involve long-running tasks, it's better to make the entire request-response process asynchronous.

Empfehlungen:Recommendations:

  • Warten Sie nicht , bis lange Ausführungsaufgaben im Rahmen der normalen Verarbeitung von HTTP-Anforderungen ausgeführt werden.Do not wait for long-running tasks to complete as part of ordinary HTTP request processing.
  • Sie sollten Anforderungen mit langer Ausführungszeit mit Hintergrund Diensten oder außerhalb des Prozesses mit einer Azure-Funktionverarbeiten.Do consider handling long-running requests with background services or out of process with an Azure Function. Das Abschließen von Arbeitsprozessen außerhalb des Prozesses ist besonders für CPU-intensive Aufgaben vorteilhaft.Completing work out-of-process is especially beneficial for CPU-intensive tasks.
  • Verwenden Sie Echt Zeit Kommunikationsoptionen, wie z SignalR . b., um asynchron mit Clients zu kommunizieren.Do use real-time communication options, such as SignalR, to communicate with clients asynchronously.

Minimieren von Client RessourcenMinify client assets

ASP.net Core-apps mit komplexen Front-Ends bedienen häufig viele JavaScript-, CSS-oder Image-Dateien.ASP.NET Core apps with complex front-ends frequently serve many JavaScript, CSS, or image files. Die Leistung der anfänglichen Ladeanforderungen kann wie folgt verbessert werden:Performance of initial load requests can be improved by:

  • Bündelung, bei der mehrere Dateien zu einer kombiniert werden.Bundling, which combines multiple files into one.
  • Minimieren: Dadurch wird die Größe der Dateien reduziert, indem Leerzeichen und Kommentare entfernt werden.Minifying, which reduces the size of files by removing whitespace and comments.

Empfehlungen:Recommendations:

  • Verwenden Sie ASP.net Core die integrierte Unterstützung für das bündeln und minimieren von Client Ressourcen.Do use ASP.NET Core's built-in support for bundling and minifying client assets.
  • Verwenden Sie andere Tools von Drittanbietern, z. b. WebPack, bei der komplexen Verwaltung von Client Assets.Do consider other third-party tools, such as Webpack, for complex client asset management.

Antworten komprimierenCompress responses

Die Reduzierung der Antwort Größe erhöht in der Regel die Reaktionsfähigkeit einer APP, oft erheblich.Reducing the size of the response usually increases the responsiveness of an app, often dramatically. Eine Möglichkeit, die Nutz Last Größen zu reduzieren, ist das Komprimieren der Antworten einer App.One way to reduce payload sizes is to compress an app's responses. Weitere Informationen finden Sie unter Antwort Komprimierung.For more information, see Response compression.

Neueste ASP.net Core Release verwendenUse the latest ASP.NET Core release

Jede neue Version von ASP.net Core umfasst Leistungsverbesserungen.Each new release of ASP.NET Core includes performance improvements. Optimierungen in .net Core und ASP.net Core bedeuten, dass neuere Versionen in der Regel ältere Versionen übersteigen.Optimizations in .NET Core and ASP.NET Core mean that newer versions generally outperform older versions. .Net Core 2,1 hat z. b. Unterstützung für kompilierte reguläre Ausdrücke hinzugefügt und von der Spanne <T> profitiert.For example, .NET Core 2.1 added support for compiled regular expressions and benefitted from Span<T>. ASP.net Core 2,2 hat Unterstützung für http/2 hinzugefügt.ASP.NET Core 2.2 added support for HTTP/2. ASP.net Core 3,0 bietet viele Verbesserungen , die die Speicherauslastung reduzieren und den Durchsatz verbessern.ASP.NET Core 3.0 adds many improvements that reduce memory usage and improve throughput. Wenn Leistung eine Priorität ist, sollten Sie ein Upgrade auf die aktuelle Version von ASP.net Core durchführen.If performance is a priority, consider upgrading to the current version of ASP.NET Core.

Minimieren von AusnahmenMinimize exceptions

Ausnahmen sollten selten vorkommen.Exceptions should be rare. Das Auslösen und Abfangen von Ausnahmen ist relativ zu anderen Code Fluss Mustern langsam.Throwing and catching exceptions is slow relative to other code flow patterns. Aus diesem Grund sollten Ausnahmen nicht zum Steuern des normalen Programmflusses verwendet werden.Because of this, exceptions shouldn't be used to control normal program flow.

Empfehlungen:Recommendations:

  • Verwenden Sie das Auslösen oder Abfangen von Ausnahmen nicht als Mittel des normalen Programmflusses, insbesondere in aktiven Codepfade.Do not use throwing or catching exceptions as a means of normal program flow, especially in hot code paths.
  • Fügen Sie Logik in die APP ein, um Bedingungen zu erkennen und zu behandeln, die eine Ausnahme auslösen würden.Do include logic in the app to detect and handle conditions that would cause an exception.
  • Lösen Sie Ausnahmen für ungewöhnliche oder unerwartete Bedingungen aus, oder fangen Sie Sie ab.Do throw or catch exceptions for unusual or unexpected conditions.

App-Diagnosetools, wie z. b. Application Insights, können helfen, allgemeine Ausnahmen in einer APP zu identifizieren, die sich auf die Leistung auswirken könnenApp diagnostic tools, such as Application Insights, can help to identify common exceptions in an app that may affect performance.

Leistung und ZuverlässigkeitPerformance and reliability

In den folgenden Abschnitten finden Sie Leistungs Tipps und bekannte Zuverlässigkeitsprobleme und-Lösungen.The following sections provide performance tips and known reliability problems and solutions.

Vermeiden Sie synchronen Lese-oder Schreibzugriff auf den HttpRequest/HttpResponse-TextAvoid synchronous read or write on HttpRequest/HttpResponse body

Alle e/a-Vorgänge in ASP.net Core sind asynchron.All I/O in ASP.NET Core is asynchronous. Server implementieren die Stream -Schnittstelle, die sowohl synchrone als auch asynchrone über Ladungen aufweist.Servers implement the Stream interface, which has both synchronous and asynchronous overloads. Die asynchronen sollten bevorzugt werden, um zu verhindern, dass Threads im Thread Pool blockiert werden.The asynchronous ones should be preferred to avoid blocking thread pool threads. Blockierende Threads können zu einem Thread Pool-Hunger führen.Blocking threads can lead to thread pool starvation.

Gehen Sie nicht wie folgt vor : Im folgenden Beispiel wird verwendet ReadToEnd .Do not do this: The following example uses the ReadToEnd. Der aktuelle Thread wird blockiert, um auf das Ergebnis zu warten.It blocks the current thread to wait for the result. Dies ist ein Beispiel für die Synchronisierung über Async.This is an example of sync over async.

public class BadStreamReaderController : Controller
{
    [HttpGet("/contoso")]
    public ActionResult<ContosoData> Get()
    {
        var json = new StreamReader(Request.Body).ReadToEnd();

        return JsonSerializer.Deserialize<ContosoData>(json);
    }
}

Im vorangehenden Code Get Liest synchron den gesamten HTTP-Anforderungs Text in den Arbeitsspeicher.In the preceding code, Get synchronously reads the entire HTTP request body into memory. Wenn der Client langsam hochgeladen wird, erfolgt die Synchronisierung der APP über Async.If the client is slowly uploading, the app is doing sync over async. Die APP führt eine Synchronisierung über Async aus, da Kestrel keine synchronen Lesevorgänge unterstützt.The app does sync over async because Kestrel does NOT support synchronous reads.

Führen Sie Folgendes aus: Im folgenden Beispiel ReadToEndAsync wird der Thread beim Lesen verwendet und nicht blockiert.Do this: The following example uses ReadToEndAsync and does not block the thread while reading.

public class GoodStreamReaderController : Controller
{
    [HttpGet("/contoso")]
    public async Task<ActionResult<ContosoData>> Get()
    {
        var json = await new StreamReader(Request.Body).ReadToEndAsync();

        return JsonSerializer.Deserialize<ContosoData>(json);
    }

}

Der vorangehende Code liest den gesamten HTTP-Anforderungs Text asynchron in den Arbeitsspeicher.The preceding code asynchronously reads the entire HTTP request body into memory.

Warnung

Wenn die Anforderung groß ist, kann das Lesen des gesamten HTTP-Anforderungs Texts in den Arbeitsspeicher zu einer OOM-Bedingung (Out of Memory) führen.If the request is large, reading the entire HTTP request body into memory could lead to an out of memory (OOM) condition. OOM kann zu einem Denial-of-Service-Ergebnis führen.OOM can result in a Denial Of Service. Weitere Informationen finden Sie unter vermeiden des Lesens großer Anforderungs Texte oder Antwort Texte in den Arbeitsspeicher in diesem Dokument.For more information, see Avoid reading large request bodies or response bodies into memory in this document.

Führen Sie Folgendes aus: Das folgende Beispiel ist vollständig asynchron und verwendet einen nicht gepufferten Anforderungs Text:Do this: The following example is fully asynchronous using a non buffered request body:

public class GoodStreamReaderController : Controller
{
    [HttpGet("/contoso")]
    public async Task<ActionResult<ContosoData>> Get()
    {
        return await JsonSerializer.DeserializeAsync<ContosoData>(Request.Body);
    }
}

Der vorangehende Code deserialisiert den Anforderungs Text asynchron in ein c#-Objekt.The preceding code asynchronously de-serializes the request body into a C# object.

"Read Form Async" über "Request. Form" bevorzugenPrefer ReadFormAsync over Request.Form

Verwenden Sie HttpContext.Request.ReadFormAsync anstelle von HttpContext.Request.Form.Use HttpContext.Request.ReadFormAsync instead of HttpContext.Request.Form. HttpContext.Request.Form kann nur mit den folgenden Bedingungen sicher gelesen werden:HttpContext.Request.Form can be safely read only with the following conditions:

  • Das Formular wurde durch einen-Rückruf gelesen ReadFormAsync , undThe form has been read by a call to ReadFormAsync, and
  • Der zwischengespeicherte Formular Wert wird gelesen mithilfe von. HttpContext.Request.FormThe cached form value is being read using HttpContext.Request.Form

Gehen Sie nicht wie folgt vor : Im folgenden Beispiel wird verwendet HttpContext.Request.Form .Do not do this: The following example uses HttpContext.Request.Form. HttpContext.Request.Form verwendet Sync über Async und kann zu einem Thread Pool-Hunger führen.HttpContext.Request.Form uses sync over async and can lead to thread pool starvation.

public class BadReadController : Controller
{
    [HttpPost("/form-body")]
    public IActionResult Post()
    {
        var form =  HttpContext.Request.Form;

        Process(form["id"], form["name"]);

        return Accepted();
    }

Führen Sie Folgendes aus: Im folgenden Beispiel wird verwendet HttpContext.Request.ReadFormAsync , um den Formular Text asynchron zu lesen.Do this: The following example uses HttpContext.Request.ReadFormAsync to read the form body asynchronously.

public class GoodReadController : Controller
{
    [HttpPost("/form-body")]
    public async Task<IActionResult> Post()
    {
       var form = await HttpContext.Request.ReadFormAsync();

        Process(form["id"], form["name"]);

        return Accepted();
    }

Vermeiden Sie, große Anforderungs Texte oder Antwort Texte in den Speicher zu lesen.Avoid reading large request bodies or response bodies into memory

In .net endet jede Objekt Zuordnung, die größer als 85 KB ist, im großen Objekt Heap (Loh).In .NET, every object allocation greater than 85 KB ends up in the large object heap (LOH). Große Objekte sind auf zwei Arten kostspielig:Large objects are expensive in two ways:

  • Die Zuordnungs Kosten sind hoch, da der Arbeitsspeicher für ein neu zugeordneter großes Objekt gelöscht werden muss.The allocation cost is high because the memory for a newly allocated large object has to be cleared. Die CLR garantiert, dass der Arbeitsspeicher für alle neu zugeordneten Objekte gelöscht wird.The CLR guarantees that memory for all newly allocated objects is cleared.
  • Loh wird mit dem restlichen Heap gesammelt.LOH is collected with the rest of the heap. Loh erfordert eine vollständige Garbage Collection -oder Gen2-Auflistung.LOH requires a full garbage collection or Gen2 collection.

In diesem Blogbeitrag wird das Problem kurz beschrieben:This blog post describes the problem succinctly:

Wenn ein großes Objekt zugewiesen wird, wird es als Objekt der Generation 2 markiert.When a large object is allocated, it's marked as Gen 2 object. Nicht Gen 0 wie bei kleinen Objekten.Not Gen 0 as for small objects. Wenn nicht genügend Arbeitsspeicher in Loh vorhanden ist, bereinigt GC den gesamten verwalteten Heap, nicht nur Loh.The consequences are that if you run out of memory in LOH, GC cleans up the whole managed heap, not only LOH. Die Generation 0, Gen 1 und Gen 2 einschließlich Loh werden bereinigt.So it cleans up Gen 0, Gen 1 and Gen 2 including LOH. Dies wird als voll Garbage Collection bezeichnet und ist der zeitraubende Garbage Collection.This is called full garbage collection and is the most time-consuming garbage collection. Für viele Anwendungen ist dies akzeptabel.For many applications, it can be acceptable. Aber definitiv nicht für Hochleistungs-Webserver, bei denen einige große Speicherpuffer benötigt werden, um eine durchschnittliche Webanforderung zu verarbeiten (Lesen Sie aus einem Socket, dekomprimieren und JSON-decodieren &).But definitely not for high-performance web servers, where few big memory buffers are needed to handle an average web request (read from a socket, decompress, decode JSON & more).

Das naive Speichern eines großen Anforderungs-oder Antwort Texts in einem einzelnen-oder-Text byte[] string :Naively storing a large request or response body into a single byte[] or string:

  • Kann dazu führen, dass der Platz in Loh schnell nicht mehr genügend Speicherplatz hat.May result in quickly running out of space in the LOH.
  • Kann aufgrund vollständiger GCS-Ausführung zu Leistungsproblemen bei der APP führen.May cause performance issues for the app because of full GCs running.

Arbeiten mit einer synchronen Datenverarbeitungs-APIWorking with a synchronous data processing API

Wenn Sie ein Serialisierungsprogramm/Deserialisierungsprogramm verwenden, das nur synchrone Lese-und Schreibvorgänge unterstützt (z. b. JSON.net):When using a serializer/de-serializer that only supports synchronous reads and writes (for example, JSON.NET):

  • Puffert die Daten asynchron in den Arbeitsspeicher, bevor Sie an das Serialisierungsprogramm/Deserialisierungsprogramm übergeben werden.Buffer the data into memory asynchronously before passing it into the serializer/de-serializer.

Warnung

Wenn die Anforderung groß ist, kann dies zu einer OOM-Bedingung (Out of Memory) führen.If the request is large, it could lead to an out of memory (OOM) condition. OOM kann zu einem Denial-of-Service-Ergebnis führen.OOM can result in a Denial Of Service. Weitere Informationen finden Sie unter vermeiden des Lesens großer Anforderungs Texte oder Antwort Texte in den Arbeitsspeicher in diesem Dokument.For more information, see Avoid reading large request bodies or response bodies into memory in this document.

ASP.net Core 3,0 verwendet System.Text.Json standardmäßig für die JSON-Serialisierung.ASP.NET Core 3.0 uses System.Text.Json by default for JSON serialization. System.Text.Json:System.Text.Json:

  • Lese- und Schreibvorgänge in JSON erfolgen asynchron.Reads and writes JSON asynchronously.
  • Der Code ist für UTF-8-Text optimiert.Is optimized for UTF-8 text.
  • In der Regel lässt sich eine höhere Leistung als mit Newtonsoft.Json erzielen.Typically higher performance than Newtonsoft.Json.

Speichern Sie ihttpcontextaccessor. HttpContext nicht in einem Feld.Do not store IHttpContextAccessor.HttpContext in a field

Der ihttpcontextaccessor. HttpContext gibt den HttpContext der aktiven Anforderung zurück, wenn der Zugriff über den Anforderungs Thread erfolgt.The IHttpContextAccessor.HttpContext returns the HttpContext of the active request when accessed from the request thread. Der IHttpContextAccessor.HttpContext darf nicht in einem Feld oder einer Variablen gespeichert werden.The IHttpContextAccessor.HttpContext should not be stored in a field or variable.

Gehen Sie nicht wie folgt vor : Im folgenden Beispiel wird der HttpContext in einem Feld gespeichert, und anschließend wird versucht, ihn später zu verwenden.Do not do this: The following example stores the HttpContext in a field and then attempts to use it later.

public class MyBadType
{
    private readonly HttpContext _context;
    public MyBadType(IHttpContextAccessor accessor)
    {
        _context = accessor.HttpContext;
    }

    public void CheckAdmin()
    {
        if (!_context.User.IsInRole("admin"))
        {
            throw new UnauthorizedAccessException("The current user isn't an admin");
        }
    }
}

Der vorangehende Code erfasst häufig einen NULL-Wert oder einen falschen Wert HttpContext im Konstruktor.The preceding code frequently captures a null or incorrect HttpContext in the constructor.

Führen Sie Folgendes aus: Im folgenden Beispiel wird Folgendes veranschaulicht:Do this: The following example:

  • Speichert den IHttpContextAccessor in einem-Feld.Stores the IHttpContextAccessor in a field.
  • Verwendet das HttpContext Feld zur richtigen Zeit und überprüft, ob null .Uses the HttpContext field at the correct time and checks for null.
public class MyGoodType
{
    private readonly IHttpContextAccessor _accessor;
    public MyGoodType(IHttpContextAccessor accessor)
    {
        _accessor = accessor;
    }

    public void CheckAdmin()
    {
        var context = _accessor.HttpContext;
        if (context != null && !context.User.IsInRole("admin"))
        {
            throw new UnauthorizedAccessException("The current user isn't an admin");
        }
    }
}

Nicht auf "HttpContext" aus mehreren Threads zugreifenDo not access HttpContext from multiple threads

HttpContext ist nicht Thread sicher.HttpContext is NOT thread-safe. Der HttpContext parallele Zugriff von mehreren Threads kann zu nicht definiertem Verhalten wie z. b. hängen, abstürzen und Daten Beschädigung führen.Accessing HttpContext from multiple threads in parallel can result in undefined behavior such as hangs, crashes, and data corruption.

Gehen Sie nicht wie folgt vor : Im folgenden Beispiel werden drei parallele Anforderungen erstellt, und der eingehende Anforderungs Pfad wird vor und nach der ausgehenden HTTP-Anforderung protokolliert.Do not do this: The following example makes three parallel requests and logs the incoming request path before and after the outgoing HTTP request. Der Zugriff auf den Anforderungs Pfad erfolgt über mehrere Threads, die möglicherweise parallel erfolgen.The request path is accessed from multiple threads, potentially in parallel.

public class AsyncBadSearchController : Controller
{       
    [HttpGet("/search")]
    public async Task<SearchResults> Get(string query)
    {
        var query1 = SearchAsync(SearchEngine.Google, query);
        var query2 = SearchAsync(SearchEngine.Bing, query);
        var query3 = SearchAsync(SearchEngine.DuckDuckGo, query);

        await Task.WhenAll(query1, query2, query3);

        var results1 = await query1;
        var results2 = await query2;
        var results3 = await query3;

        return SearchResults.Combine(results1, results2, results3);
    }       

    private async Task<SearchResults> SearchAsync(SearchEngine engine, string query)
    {
        var searchResults = _searchService.Empty();
        try
        {
            _logger.LogInformation("Starting search query from {path}.", 
                                    HttpContext.Request.Path);
            searchResults = _searchService.Search(engine, query);
            _logger.LogInformation("Finishing search query from {path}.", 
                                    HttpContext.Request.Path);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed query from {path}", 
                             HttpContext.Request.Path);
        }

        return await searchResults;
    }

Führen Sie Folgendes aus: Im folgenden Beispiel werden alle Daten aus der eingehenden Anforderung kopiert, bevor die drei parallelen Anforderungen vorgenommen werden.Do this: The following example copies all data from the incoming request before making the three parallel requests.

public class AsyncGoodSearchController : Controller
{       
    [HttpGet("/search")]
    public async Task<SearchResults> Get(string query)
    {
        string path = HttpContext.Request.Path;
        var query1 = SearchAsync(SearchEngine.Google, query,
                                 path);
        var query2 = SearchAsync(SearchEngine.Bing, query, path);
        var query3 = SearchAsync(SearchEngine.DuckDuckGo, query, path);

        await Task.WhenAll(query1, query2, query3);

        var results1 = await query1;
        var results2 = await query2;
        var results3 = await query3;

        return SearchResults.Combine(results1, results2, results3);
    }

    private async Task<SearchResults> SearchAsync(SearchEngine engine, string query,
                                                  string path)
    {
        var searchResults = _searchService.Empty();
        try
        {
            _logger.LogInformation("Starting search query from {path}.",
                                   path);
            searchResults = await _searchService.SearchAsync(engine, query);
            _logger.LogInformation("Finishing search query from {path}.", path);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed query from {path}", path);
        }

        return await searchResults;
    }

Verwenden Sie HttpContext nicht, wenn die Anforderung erfüllt ist.Do not use the HttpContext after the request is complete

HttpContext ist nur gültig, solange eine aktive HTTP-Anforderung in der ASP.net Core Pipeline vorhanden ist.HttpContext is only valid as long as there is an active HTTP request in the ASP.NET Core pipeline. Die gesamte ASP.net Core Pipeline ist eine asynchrone Kette von Delegaten, die jede Anforderung ausführt.The entire ASP.NET Core pipeline is an asynchronous chain of delegates that executes every request. Wenn die Task von dieser Kette zurückgegebene abgeschlossen wird, wird der wieder verwendet HttpContext .When the Task returned from this chain completes, the HttpContext is recycled.

Gehen Sie nicht wie folgt vor : Im folgenden Beispiel async void wird verwendet, wodurch die HTTP-Anforderung durchgeführt wird, wenn die erste await erreicht wird:Do not do this: The following example uses async void which makes the HTTP request complete when the first await is reached:

  • Dies ist in ASP.net Core-apps immer eine bewährte Vorgehensweise.Which is ALWAYS a bad practice in ASP.NET Core apps.
  • Greift auf den zu, HttpResponse nachdem die HTTP-Anforderung beendet wurde.Accesses the HttpResponse after the HTTP request is complete.
  • Stürzt den Prozess ab.Crashes the process.
public class AsyncBadVoidController : Controller
{
    [HttpGet("/async")]
    public async void Get()
    {
        await Task.Delay(1000);

        // The following line will crash the process because of writing after the 
        // response has completed on a background thread. Notice async void Get()

        await Response.WriteAsync("Hello World");
    }
}

Führen Sie Folgendes aus: Im folgenden Beispiel wird ein-Wert Task an das Framework zurückgegeben, sodass die HTTP-Anforderung erst abgeschlossen wird, wenn die Aktion abgeschlossen ist.Do this: The following example returns a Task to the framework, so the HTTP request doesn't complete until the action completes.

public class AsyncGoodTaskController : Controller
{
    [HttpGet("/async")]
    public async Task Get()
    {
        await Task.Delay(1000);

        await Response.WriteAsync("Hello World");
    }
}

HttpContext nicht in Hintergrundthreads erfassenDo not capture the HttpContext in background threads

Gehen Sie nicht wie folgt vor : Im folgenden Beispiel wird gezeigt, dass das Aufzeichnen HttpContext von aus der- Controller Eigenschaft erfolgt.Do not do this: The following example shows a closure is capturing the HttpContext from the Controller property. Dies ist eine bewährte Vorgehensweise, da das Arbeits Element folgende Aktionen ausführen könnte:This is a bad practice because the work item could:

  • Außerhalb des Anforderungs Bereichs ausführen.Run outside of the request scope.
  • Versuchen Sie, den Fehler zu lesen HttpContext .Attempt to read the wrong HttpContext.
[HttpGet("/fire-and-forget-1")]
public IActionResult BadFireAndForget()
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        var path = HttpContext.Request.Path;
        Log(path);
    });

    return Accepted();
}

Führen Sie Folgendes aus: Im folgenden Beispiel wird Folgendes veranschaulicht:Do this: The following example:

  • Kopiert die Daten, die in der Hintergrundaufgabe während der Anforderung erforderlich sind.Copies the data required in the background task during the request.
  • Verweist auf nichts vom Controller.Doesn't reference anything from the controller.
[HttpGet("/fire-and-forget-3")]
public IActionResult GoodFireAndForget()
{
    string path = HttpContext.Request.Path;
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        Log(path);
    });

    return Accepted();
}

Hintergrundaufgaben sollten als gehostete Dienste implementiert werden.Background tasks should be implemented as hosted services. Weitere Informationen finden Sie unter Hintergrundaufgaben mit gehosteten Diensten.For more information, see Background tasks with hosted services.

Dienste, die in die Controller in Hintergrundthreads eingefügt werden, nicht erfassenDo not capture services injected into the controllers on background threads

Gehen Sie nicht wie folgt vor : Im folgenden Beispiel wird gezeigt, dass das Aufzeichnen DbContext von aus dem Controller Action-Parameter erfolgt.Do not do this: The following example shows a closure is capturing the DbContext from the Controller action parameter. Dies ist eine bewährte Vorgehensweise.This is a bad practice. Das Arbeits Element kann außerhalb des Anforderungs Bereichs ausgeführt werden.The work item could run outside of the request scope. Der bezieht sich ContosoDbContext auf die Anforderung, was zu einer führt ObjectDisposedException .The ContosoDbContext is scoped to the request, resulting in an ObjectDisposedException.

[HttpGet("/fire-and-forget-1")]
public IActionResult FireAndForget1([FromServices]ContosoDbContext context)
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        context.Contoso.Add(new Contoso());
        await context.SaveChangesAsync();
    });

    return Accepted();
}

Führen Sie Folgendes aus: Im folgenden Beispiel wird Folgendes veranschaulicht:Do this: The following example:

  • Fügt ein ein IServiceScopeFactory , um einen Bereich im Hintergrund Arbeits Element zu erstellen.Injects an IServiceScopeFactory in order to create a scope in the background work item. IServiceScopeFactory ist ein Singleton.IServiceScopeFactory is a singleton.
  • Erstellt einen neuen Abhängigkeits einschleusungs Bereich im Hintergrund Thread.Creates a new dependency injection scope in the background thread.
  • Verweist auf nichts vom Controller.Doesn't reference anything from the controller.
  • Erfasst nicht ContosoDbContext aus der eingehenden Anforderung.Doesn't capture the ContosoDbContext from the incoming request.
[HttpGet("/fire-and-forget-3")]
public IActionResult FireAndForget3([FromServices]IServiceScopeFactory 
                                    serviceScopeFactory)
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        using (var scope = serviceScopeFactory.CreateScope())
        {
            var context = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();

            context.Contoso.Add(new Contoso());

            await context.SaveChangesAsync();                                        
        }
    });

    return Accepted();
}

Der folgende markierte Code:The following highlighted code:

  • Erstellt einen Gültigkeitsbereich für die Lebensdauer des Hintergrund Vorgangs und löst Dienste davon auf.Creates a scope for the lifetime of the background operation and resolves services from it.
  • Verwendet ContosoDbContext aus dem richtigen Bereich.Uses ContosoDbContext from the correct scope.
[HttpGet("/fire-and-forget-3")]
public IActionResult FireAndForget3([FromServices]IServiceScopeFactory 
                                    serviceScopeFactory)
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        using (var scope = serviceScopeFactory.CreateScope())
        {
            var context = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();

            context.Contoso.Add(new Contoso());

            await context.SaveChangesAsync();                                        
        }
    });

    return Accepted();
}

Ändern Sie den Statuscode oder die Header nicht, nachdem der Antworttext gestartet wurde.Do not modify the status code or headers after the response body has started

Der HTTP-Antworttext wird von ASP.net Core nicht gepuffert.ASP.NET Core does not buffer the HTTP response body. Wenn die Antwort zum ersten Mal geschrieben wird:The first time the response is written:

  • Die Header werden zusammen mit diesem Textteil an den Client gesendet.The headers are sent along with that chunk of the body to the client.
  • Es ist nicht mehr möglich, Antwortheader zu ändern.It's no longer possible to change response headers.

Gehen Sie nicht wie folgt vor : Der folgende Code versucht, Antwortheader hinzuzufügen, nachdem die Antwort bereits gestartet wurde:Do not do this: The following code tries to add response headers after the response has already started:

app.Use(async (context, next) =>
{
    await next();

    context.Response.Headers["test"] = "test value";
});

Im vorangehenden Code löst context.Response.Headers["test"] = "test value"; eine Ausnahme aus, wenn in next() die Antwort geschrieben hat.In the preceding code, context.Response.Headers["test"] = "test value"; will throw an exception if next() has written to the response.

Führen Sie Folgendes aus: Im folgenden Beispiel wird überprüft, ob die HTTP-Antwort vor dem Ändern der Header gestartet wurde.Do this: The following example checks if the HTTP response has started before modifying the headers.

app.Use(async (context, next) =>
{
    await next();

    if (!context.Response.HasStarted)
    {
        context.Response.Headers["test"] = "test value";
    }
});

Führen Sie Folgendes aus: Im folgenden Beispiel HttpResponse.OnStarting wird verwendet, um die Header festzulegen, bevor die Antwortheader an den Client geleert werden.Do this: The following example uses HttpResponse.OnStarting to set the headers before the response headers are flushed to the client.

Wenn Sie überprüfen, ob die Antwort nicht gestartet wurde, können Sie einen Rückruf registrieren, der unmittelbar vor dem Schreiben von Antwort Headern aufgerufen wird.Checking if the response has not started allows registering a callback that will be invoked just before response headers are written. Es wird überprüft, ob die Antwort nicht gestartet wurde:Checking if the response has not started:

  • Bietet die Möglichkeit, Header Just-in-Time anzufügen oder zu überschreiben.Provides the ability to append or override headers just in time.
  • Erfordert keine Kenntnis der nächsten Middleware in der Pipeline.Doesn't require knowledge of the next middleware in the pipeline.
app.Use(async (context, next) =>
{
    context.Response.OnStarting(() =>
    {
        context.Response.Headers["someheader"] = "somevalue";
        return Task.CompletedTask;
    });

    await next();
});

"Next ()" nicht aufzurufen, wenn Sie bereits mit dem Schreiben in den Antworttext begonnen habenDo not call next() if you have already started writing to the response body

Komponenten erwarten nur, dass Sie aufgerufen werden, wenn Sie die Antwort verarbeiten und bearbeiten können.Components only expect to be called if it's possible for them to handle and manipulate the response.

Verwenden des in-Process-Hosting mit IISUse In-process hosting with IIS

Beim Einsatz von In-Process-Hosting wird eine ASP.NET Core-App im gleichen Prozess wie ihr IIS-Arbeitsprozess ausgeführt.Using in-process hosting, an ASP.NET Core app runs in the same process as its IIS worker process. Das in-Process-Hosting bietet eine verbesserte Leistung gegenüber dem Out-of-Process-Hosting, da Anforderungen nicht über den Loopback Adapter über einen Proxy gesendet werden.In-process hosting provides improved performance over out-of-process hosting because requests aren't proxied over the loopback adapter. Der Loopback Adapter ist eine Netzwerkschnittstelle, die ausgehenden Netzwerk Datenverkehr zurück an denselben Computer zurückgibt.The loopback adapter is a network interface that returns outgoing network traffic back to the same machine. IIS erledigt das Prozessmanagement mit dem Windows-Prozessaktivierungsdienst (Process Activation Service, WAS).IIS handles process management with the Windows Process Activation Service (WAS).

-Projekte werden standardmäßig in ASP.net Core 3,0 und höher auf das in-Process-Hostingmodell eingestellt.Projects default to the in-process hosting model in ASP.NET Core 3.0 and later.

Weitere Informationen finden Sie unter Hosten von ASP.net Core unter Windows mit IISFor more information, see Host ASP.NET Core on Windows with IIS