Version 4 (V4) von AWS SDK für .NET wurde veröffentlicht!
Informationen zu wichtigen Änderungen und zur Migration Ihrer Anwendungen finden Sie im Thema https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/net-dg-v4.html Migration.
Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.
Best Practices für die Leistung AWS SDK für .NET
Wie Sie Clients erstellen, Antworten verarbeiten und Ihre Anwendung konfigurieren, hat große Auswirkungen auf den Durchsatz, die Latenz und die Speichernutzung. In diesem Thema werden Nutzungs- und Konfigurationsmuster beschrieben, die dazu beitragen, dass Ihre Anwendungen effizient und zuverlässig ausgeführt werden. Diese Muster sind bei hoher Auslastung oder in Umgebungen mit beschränkten Ressourcen wie Containern und serverlosen Funktionen am wichtigsten. Wenn Sie diese Methoden befolgen, können Sie die Leistung verbessern und häufig auftretende Probleme wie langsame Reaktionen, Hängen und hohe Speicherauslastung verhindern.
Die wirksamsten Praktiken sind die folgenden:
-
Verwenden Sie einen einzelnen, langlebigen Service-Client wieder, anstatt einen pro Anfrage zu erstellen.
-
Löschen Sie Antworten und Streams, sodass Netzwerkverbindungen wieder zum Verbindungspool freigegeben werden.
-
Verwenden Sieasync/awaitkorrekt und blockieren Sie niemals bei asynchronen SDK-Aufrufen.
-
Konfigurieren Sie .NET-Garbage Collection für eingeschränkte Umgebungen wie Amazon ECS AWS Lambda .
-
Verwalten Sie HTTP-Verbindungen und Verbindungslimits bei hohem Durchsatz.
Bevor Sie beginnen, stellen Sie sicher, dass Sie Ihre Umgebung eingerichtet und Ihr Projekt konfiguriert haben.
Verwenden Sie einen einzigen, langlebigen Service-Client erneut
Serviceclients wie AmazonS3Client AmazonDynamoDBClient sind threadsicher, relativ teuer in der Erstellung und langlebig. Bei der Erstellung eines Clients werden Regions- und Endpunktinformationen aufgelöst und die zugrunde liegende HTTP-Infrastruktur eingerichtet. Erstellen Sie einen Client pro Dienst und verwenden Sie ihn für die gesamte Lebensdauer Ihrer Anwendung wieder. Wenn Sie mehr als eine Region aufrufen, erstellen Sie für jede Region einen eigenen Client.
Warnung
Erstellen Sie nicht für jede Anfrage oder innerhalb einer Schleife einen neuen Service-Client. Durch das wiederholte Erstellen von Clients wird die zugrunde liegende HTTP-Infrastruktur verändert, was zu einer messbaren Latenz führt. Es kann auch dazu führen, dass Sockets oder Handles überlastet werden und Anfragen fehlschlagen oder unter Last hängen bleiben.
In Anwendungen, die Dependency Injection verwenden, registrieren Sie den Client als Singleton. Die AddAWSService Erweiterungsmethode aus dem AWSSDK.Extensions.NETCore.Setup NuGet Paket registriert den Client mit einer Standardlebensdauer vonServiceLifetime.Singleton. Der Client wird erstellt, wenn er zum ersten Mal angefordert wird, und dieselbe Instanz wird für die gesamte Lebensdauer des Prozesses wiederverwendet. Weitere Hinweise zur Registrierung von AWS
Diensten mit Dependency-Injection und zum Lesen von Optionen aus der Konfiguration finden Sie unterAWSSDK.Extensions.NETCore.Setup und IConfiguration.
Sie können einen Client auch manuell als Singleton registrieren.
builder.Services.AddSingleton<IAmazonS3>(_ => new AmazonS3Client());
Anmerkung
Da der Client standardmäßig als Singleton AddAWSService registriert wird, sollten Sie den Client, den er bereitstellt, nicht entsorgen. Wenn Sie eine andere als die Standardlebensdauer benötigen, übergeben Sie dem optionalen lifetime Parameter von einen anderen ServiceLifetime Wert. AddAWSService
Die Wiederverwendung des Clients bedeutet nicht, dass Sie aufhören sollten, Antworten pro Vorgang zu löschen. Verwenden Sie den Client für die gesamte Lebensdauer der Anwendung wieder, löschen Sie jedoch weiterhin die Antworten und Streams, die von einzelnen Vorgängen zurückgegeben werden, wie unter beschrieben. Antworten und Streams löschen
Löschen Sie Antworten und Streams, um Verbindungen freizugeben
Einige SDK-Antwortobjekte übertragen einen Live-Netzwerkstream. Das gängigste Beispiel ist GetObjectResponse, das den Inhalt des Objekts mithilfe seiner ResponseStream Eigenschaft implementiert IDisposable und verfügbar macht. Die Antwort hält eine offene HTTP-Verbindung aufrecht, bis der Stream vollständig gelesen oder die Antwort gelöscht ist. Wenn Sie diese Antworten durchsickern lassen (z. B. indem Sie GetObject in einer Schleife aufrufen, ohne jedes Ergebnis zu löschen), häufen sich offene Verbindungen an, bis der Verbindungspool erschöpft ist und der nächste Anruf blockiert wird. Dies ist die Ursache für Berichte über Downloads, die „zufällig hängen“ oder auf dem N-ten Objekt zum Stillstand kommen.
Schließen Sie eine Streaming-Antwort immer in eine using Anweisung ein und lesen oder kopieren Sie den Stream umgehend.
using Amazon.S3; using Amazon.S3.Model; // s3Client is a reused, long-lived client. var request = new GetObjectRequest { BucketName = bucketName, Key = key }; using var response = await s3Client.GetObjectAsync(request); await response.WriteResponseStreamToFileAsync(filePath, append: false, CancellationToken.None);
Das Löschen der Antwort und das Löschen der Antwort ResponseStream sind gleichwertig; beide schließen den zugrunde liegenden Netzwerk-Stream und geben die Verbindung an den Pool zurück.
Tipp
Wenn Sie nur Objektmetadaten wie Größe, Uhrzeit der letzten Änderung, Inhaltstyp oder ETag benötigen, rufen Sie GetObjectMetadata stattdessen oder auf. GetObjectMetadataAsync GetObject Die Metadatenoperation gibt eine HEAD HTTP-Anfrage aus und überträgt keinen Objektkörper, sodass kein Inhaltsstream verwaltet werden muss.
var metadata = await s3Client.GetObjectMetadataAsync(bucketName, key); Console.WriteLine($"Size: {metadata.ContentLength} bytes");
Warnung
Antwort- und Streamtypen wie GetObjectResponse „keinen Finalizer“ implementieren, sodass Sie sich nicht darauf verlassen können, dass die Garbage Collection ihre Verbindungen für Sie freigibt. Sie müssen Antworten und Streams deterministisch mit using oder einem expliziten Aufruf löschen. Dispose
async/await Richtig verwenden
Auf moderner.NET AWS SDK für .NET sind die Dienstoperationen in asynchron und geben a zurück. Task In .NET Framework gibt es auch synchrone Methoden, aber asynchrone Aufrufe lassen sich besser skalieren und werden empfohlen. Führen Sie Operationen mit await jeder Ebene Ihres Codes async durch und übertragen Sie sie bis zum Einstiegspunkt. Weitere Informationen zur asynchronen Programmierung mit dem SDK finden Sie unter. Asynchrone Programmierung
Warnung
Blockieren Sie nicht bei einem asynchronen SDK-Aufruf mit.Result, oder. .Wait() .GetAwaiter().GetResult() Dieses Sync-over-Async-Muster ist eine häufige Ursache dafür, dass Anwendungen hängen bleiben:
-
Unter Last verbrauchen blockierende Aufrufe Threads schneller, als der Threadpool wachsen kann, sodass Fortsetzungen nicht ausgeführt werden können. Dieser Mangel an Threadpools wirkt wie ein unbegrenzter Stillstand und ist der vorherrschende Fehlermodus im modernen .NET (wo ASP.NET Core es keine Standardeinstellung gibt).
SynchronizationContext -
Einige Kontexte erfassen a
SynchronizationContext, z. B. klassischeASP.NET,, Windows Forms WPFBlazor WebAssembly, und.NET Framework-Anwendungen. In diesen Kontexten führt das Blockieren des aufrufenden Threads, während für eine Fortsetzung derselbe Thread benötigt wird, zu einem Deadlock. Operationen im SDK werdenConfigureAwait(false)intern verwendet, sodass sie ihre Fortsetzungen nicht in den erfassten Kontext zurücksenden. Der Deadlock entsteht durchasyncCode an einer anderen Stelle in Ihrer Aufrufkette, der ihn erfasst. Wenn Sie SDK-Aufrufe mitawaitThroughout verwenden, wird das Problem vollständig vermieden.
Die folgende Methode blockiert den asynchronen Aufruf und kann den Threadpool blockieren oder aussetzen.
// Anti-pattern: do not do this. public GetObjectResponse Get(GetObjectRequest request) { return s3Client.GetObjectAsync(request).Result; }
Führen Sie stattdessen die Methode async und den Aufruf durch. await
public async Task<GetObjectResponse> GetAsync(GetObjectRequest request) { return await s3Client.GetObjectAsync(request); }
Zusätzliche Empfehlungen für asynchronen Code:
-
Übergeben
CancellationTokenSie a an jeden Vorgang, sodass ein langsamer oder blockierter Anruf abgebrochen werden kann, anstatt zu hängen. Weitere Informationen finden Sie unter Verwenden des CancellationToken Parameters für Timeouts. -
Schlucken Sie keine Ausnahmen in einem leeren
catchBlock. Dadurch wird der eigentliche Fehler ausgeblendet und ein Hängen ist nicht mehr von einem Fehler zu unterscheiden. Fangen Sie bestimmte Ausnahmen ab und protokollieren Sie sie. -
Wenn Sie eine abgefangene Ausnahme erneut auslösen, verwenden Sie
throw;stattdessen,throw ex;damit der ursprüngliche Stack-Trace erhalten bleibt. -
Wenn Sie von einer synchronen Grenze aus aufrufen müssen, behandeln Sie dies als letzten Ausweg und isolieren Sie die Arbeit vom erfassten Kontext, anstatt blockierende Aufrufe zum Standardmuster zu machen.
Konfigurieren Sie .NET-Garbage Collection für AWS Lambda und Amazon ECS
Wenn ein Container ein „Speicherleck“ zu haben scheint, behält der.NET-Garbage Collector (GC) möglicherweise den zurückgewonnenen Speicher zur Wiederverwendung vor. Daher kann der Prozessspeicher auch dann hoch und stabil erscheinen, wenn der verwaltete Heap nicht wächst. Außerdem erkennt der GC nicht automatisch das Speicherlimit eines Containers (sein Cgroup-Limit). In einer eingeschränkten Umgebung wächst der Heap dann möglicherweise in Richtung des Speichers des Hosts und nicht in Richtung des Limits des Containers. Dies kann dazu führen, dass OutOfMemoryException oder der Container beendet wird.
Damit der GC auch in eingeschränkten Umgebungen gut funktioniert:
-
Legen Sie ein explizites Speicherlimit für den Container fest, damit der GC das Cgroup-Limit einhält, and/or setzen Sie die Umgebungsvariable
DOTNET_GCHeapHardLimit(ein absoluter Bytewert in Hexadezimalzahl) oder dieDOTNET_GCHeapHardLimitPercentUmgebungsvariable, um den verwalteten Heap zu begrenzen. In mehreren gemeldeten Fällen, in denen Amazon ECS nicht über genügend Arbeitsspeicher verfügte, wurden die Abstürze durch das Setzen eines Festplattenspeicherlimits behoben. -
Auf kleinen Hosts AWS Lambda und Amazon ECS-Hosts sollten Sie erwägen, die gleichzeitige Garbage Collection (im Hintergrund) zu deaktivieren, damit der Collector keinen zusätzlichen Speicher reserviert. Setzen Sie beispielsweise die
DOTNET_gcConcurrentUmgebungsvariable auf0oder legen Sie sie<ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>in der Projektdatei fest. -
Beschränken Sie Ihre Parallelität. Wenn Sie viele Operationen gleichzeitig ausführen, z. B. das Aufrufen einer
Task.WhenAllgroßen Sammlung, wird der Prozessspeicher aufgebläht und der Verbindungspool kann beeinträchtigt werden. Beschränken Sie stattdessen den Grad der Parallelität. Vermeiden Sie beispielsweise dieses unbegrenzte Muster:// Anti-pattern: starts one task per item with no limit. await Task.WhenAll(keys.Select(key => s3Client.GetObjectMetadataAsync(bucket, key)));Begrenzen Sie stattdessen die Parallelität mit:
Parallel.ForEachAsyncvar options = new ParallelOptions { MaxDegreeOfParallelism = 10 }; await Parallel.ForEachAsync(keys, options, async (key, token) => { await s3Client.GetObjectMetadataAsync(bucket, key, token); });
Weitere Informationen zu diesen Einstellungen finden Sie unter Laufzeitkonfigurationsoptionen für die Garbage Collection
HTTP-Verbindungen und Verbindungslimits verwalten
Bei hohem Durchsatz treten häufig zwei Verbindungsprobleme auf. Die erste besteht darin, zu viele kurzlebige Verbindungen zu öffnen. Dadurch werden kurzlebige Ports erschöpft, Sockets bleiben erhalten und die TCP- und TIME_WAIT TLS-Handshake-Latenz wird erhöht. Die zweite besteht darin, dass zu wenige Verbindungen verfügbar sind, wodurch die Parallelität behindert wird. Die Wiederverwendung eines einzelnen, langlebigen Clients (sieheServicekunden wiederverwenden) ist die Grundlage für ein einwandfreies Verbindungspooling, da das Pooling davon abhängt, dass der Client wiederverwendet wird.
Um die Anzahl gleichzeitiger Verbindungen pro Endpunkt zu optimieren, legen Sie die Eigenschaft in der MaxConnectionsPerServer Client-Konfiguration fest. Wenn diese Eigenschaft null (die Standardeinstellung) ist, gilt die zugrundeliegende HttpClientHandler Standardeinstellung, die auf moderner.NET praktisch unbegrenzt ist. Erhöhen Sie es nur, wenn bei vielen gleichzeitigen Anfragen an denselben Endpunkt ein Verbindungsengpass auftritt. Ein guter Ausgangspunkt ist die maximale Anzahl gleichzeitiger Anfragen, die Sie pro Endpunkt erwarten. Wenn Sie sie deutlich höher einstellen, als Ihre Arbeitslast benötigt, werden Sockets verschwendet, ohne den Durchsatz zu verbessern.
using Amazon.S3; var config = new AmazonS3Config { MaxConnectionsPerServer = 50 }; var s3Client = new AmazonS3Client(config);
Wenn Sie Dependency Injection bereits verwenden, konfigurieren Sie Ihre Clients überAWSSDK.Extensions.NETCore.Setup. Dies ist die empfohlene Vorgehensweise, wenn Sie DI verwenden oder mehrere Service-Clients registrieren. Es zentralisiert die Konfiguration und erleichtert das Einfügen und Testen von Clients. Sie können Konfigurationswerte in der Konfiguration Ihrer Anwendung und nicht im Code festlegen. Weitere Informationen finden Sie unter AWSSDK.Extensions.NETCore.Setup und IConfiguration.
Schließlich sollten Sie Ihre eigene Parallelität so festlegen, dass Sie nicht mehr gleichzeitige Operationen starten, als Ihr Verbindungslimit zulässt. Zum Beispiel Gate-Aufrufe mit einer SemaphoreSlim Größe, die auf Ihr Verbindungslimit begrenzt ist:
var throttle = new SemaphoreSlim(50); // match MaxConnectionsPerServer await throttle.WaitAsync(token); try { await s3Client.GetObjectAsync(request, token); } finally { throttle.Release(); }
Konfigurieren Sie Timeouts und Wiederholungen
Timeouts und Wiederholungen wirken sich direkt auf die wahrgenommene Leistung aus. Ein zu hoher Timeout Wert führt dazu, dass eine angehaltene Anfrage für eine lange Zeit blockiert wird. Wenn ein Dienst bereits Drosselungsfehler zurückgibt, kommen durch eine aggressive Wiederholungsrichtlinie weitere Anfragen hinzu, was die Drosselung verschlimmern kann. Wählen Sie eine Wiederholungsrichtlinie, die der Toleranz Ihrer Anwendung in Bezug auf Latenz und Ausfall entspricht, und lassen Sie zu, dass sich echte Ausnahmen ausbreiten, anstatt es so zu wiederholen, dass ein Stillstand maskiert wird.
Anmerkung
Die Timeout Eigenschaft wirkt sich nicht auf asynchrone Aufrufe aus. Wenn Sie asynchrone Aufrufe verwenden, lesen Sie stattdessen nach. Verwenden des CancellationToken Parameters für Timeouts
Weitere Hinweise zu den Wiederholungsmodi und der Timeout Eigenschaft (ReadWriteTimeoutgilt nur für.NET Framework) sowie Beispiele für deren Einstellung finden Sie unter. MaxErrorRetry Wiederholungen und Timeouts
Optimieren Sie Streaming und Übertragungen großer Objekte (Amazon S3)
Verwenden Sie die TransferUtility Klasse im Amazon.S3.Transfer Namespace, um große Objekte oder viele Objekte hoch- und herunterzuladen. Sie lädt mithilfe mehrteiliger Übertragungen parallel hoch und lädt sie herunter und verwaltet Streams, Teile und Verbindungen für Sie. Das ist schneller als eine Single-Stream-Übertragung und ist die empfohlene Methode, um große Objekte zu verschieben.
-
Paralleler mehrteiliger Download. Frühere Versionen des SDK haben ein Objekt als einzelnen Stream heruntergeladen, anstatt Teile parallel herunterzuladen. Ab
AWSSDK.S3Version 4.0.17TransferUtilitybietet es einen mehrteiligen (parallelen) Download über die MethodenDownloadWithResponseAsyncOpenStreamWithResponseAsync, und.DownloadDirectoryWithResponseAsyncBei Verwendung werden die Teile des Objekts im Arbeitsspeicher zwischengespeichertOpenStreamWithResponseAsync, während Sie den zurückgegebenen Stream konsumieren. Steuern Sie mit derMaxInMemoryPartsEigenschaft von, wie viele Teile gepuffert werden. TransferUtilityOpenStreamRequest Für die meisten Übertragungen bevorzugen SieTransferUtility. Laden Sie Bytebereiche selbst mit derByteRangeEigenschaft herunter, GetObjectRequest nur dann, wenn Sie einen bestimmten Bereich oder ein benutzerdefiniertes Parallelitätsschema benötigen. Weitere Informationen finden Sie im AWS Developer Tools-Blog unter Einführung der Unterstützung für mehrteilige Downloads für AWS SDK for .NET Transfer Manager. -
Länge des Inhalts für Uploads. Ein Amazon S3
PUTerfordert eine bekannte Inhaltslänge, und das SDK berechnet standardmäßig eine Prüfsumme über den Anforderungstext. Wenn die Länge bekannt ist und der Stream durchsuchbar ist, kann das SDK dies tun, ohne das gesamte Objekt im Speicher zwischenspeichern zu müssen. DasTransferUtilityverarbeitet Streams, die nicht durchsucht werden können, für Sie, indem es nach Bedarf puffert. Wenn Sie stattdessenPutObjectAsyncdirekt mit einem Stream aufrufen, der nicht durchsucht werden kann (z. B. ein unformatierter AnforderungstexteingangASP.NET Core), kann die Anfrage fehlschlagen. Stellen Sie einen durchsuchbaren Stream bereit, legen Sie die Inhaltslänge explizit fest oder konfigurieren Sie, wie das SDK Prüfsummen berechnet. Weitere Informationen finden Sie im Referenzhandbuch für AWS SDKs und Tools unter Schutz der Datenintegrität. -
Dimensionierung des Bauteils. Amazon S3 erlaubt maximal 10.000 Teile pro mehrteiligem Upload. Wenn die Gesamtlänge bekannt ist, berechnet das SDK automatisch eine Teilgröße, die innerhalb dieser Grenze bleibt. Sie müssen
PartSizesich hauptsächlich auf Streams einstellen, deren Länge nicht im Voraus bekannt ist. Andernfalls können kleine Standardteile das Limit bei einem sehr großen Upload überschreiten. Eine größere Bauteilgröße reduziert auch den Mehraufwand pro Teil, was wiederum zu einem höheren Speicherbedarf pro Teil führt.
Diagnostizieren Sie Leistungsprobleme
Wenn Sie eine Verlangsamung, ein Stillstand oder ein offensichtliches Leck untersuchen, helfen Ihnen die folgenden Signale, die Ursache schnell zu finden:
-
Eine wachsende Anzahl von Sockets im
TIME_WAITStatusCLOSE_WAITOR (sichtbar mitnetstat) ist der Fingerabdruck nicht gelöster Antworten oder von Clients, die pro Anfrage erstellt und gelöscht werden. Siehe Antworten und Streams löschen und Servicekunden wiederverwenden. -
Aktivieren Sie Anforderungsmetriken und Antwortprotokollierung, um zu bestätigen, dass Anfragen tatsächlich versendet werden, und um die Latenz zu messen. Legen Sie die Eigenschaften des LoggingConfig Objekts in AWSConfigs fest, bevor Sie Ihre Service-Clients erstellen. Ein Client erfasst Einstellungen, z. B.
LogMetricswenn er erstellt wird.using Amazon; AWSConfigs.LoggingConfig.LogMetrics = true; AWSConfigs.LoggingConfig.LogResponses = ResponseLoggingOption.OnError; -
Unterscheiden Sie bei der Untersuchung des Speichers den gesamten Prozessspeicher vom verwalteten Heap. Ein hoher, aber stabiler Prozessspeicher ist oft eher der GC, der den wiedergewonnenen Speicher enthält, als ein Leck. Siehe Konfigurieren Sie die Müllabfuhr.