View a markdown version of this page

Le migliori pratiche prestazionali per AWS SDK per .NET - AWS SDK per .NET (V4)

La versione 4 (V4) di è stata rilasciata! AWS SDK per .NET

Per informazioni sulle modifiche sostanziali e sulla migrazione delle applicazioni, consulta l'argomento sulla migrazione.

Orange button with text "Click here for details".

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

Le migliori pratiche prestazionali per AWS SDK per .NET

Il modo in cui create i client, gestite le risposte e configurate l'applicazione ha un grande effetto sulla velocità effettiva, sulla latenza e sull'uso della memoria. Questo argomento descrive i modelli di utilizzo e configurazione che aiutano le applicazioni a funzionare in modo efficiente e affidabile. Questi modelli sono particolarmente importanti in condizioni di carico elevato o in ambienti con risorse limitate, come contenitori e funzioni serverless. Seguendo queste pratiche è possibile migliorare le prestazioni e prevenire problemi comuni come risposte lente, blocchi e uso elevato della memoria.

Le pratiche più efficaci sono le seguenti:

Prima di iniziare, assicurati di aver impostato il tuo ambiente e configurato il tuo progetto.

Riutilizza un singolo client di servizio di lunga durata

I client di servizio come AmazonS3Client AmazonDynamoDBClient sono thread-safe, relativamente costosi da costruire e duraturi. La creazione di un client risolve le informazioni sulla regione e sugli endpoint e stabilisce l'infrastruttura HTTP sottostante. Crea un client per servizio e riutilizzalo per tutta la durata dell'applicazione. Se chiami più di una regione, crea un client separato per ciascuna regione.

avvertimento

Non creare un nuovo client di servizio per ogni richiesta o all'interno di un ciclo. La creazione ripetuta di client altera l'infrastruttura HTTP sottostante, aggiungendo una latenza misurabile. Inoltre, può esaurire socket o handle e causare il fallimento o il blocco delle richieste sotto carico.

Nelle applicazioni che utilizzano l'iniezione di dipendenze, registrate il client come singleton. Il metodo di AddAWSService estensione del AWSSDK.Extensions.NETCore.Setup NuGet pacchetto registra il client con una durata predefinita di. ServiceLifetime.Singleton Il client viene creato la prima volta che viene richiesto e la stessa istanza viene riutilizzata per tutta la durata del processo. Per ulteriori informazioni sulla registrazione AWS dei servizi con inserimento delle dipendenze e sulle opzioni di lettura della configurazione, vedere. AWSSDK.Extensions.NETCore.Setup e iConfiguration

È inoltre possibile registrare un client come singleton manualmente.

builder.Services.AddSingleton<IAmazonS3>(_ => new AmazonS3Client());
Nota

Poiché per impostazione predefinita AddAWSService registra il client come singleton, non eliminate il client che fornisce. Se hai bisogno di una durata non predefinita, passa un ServiceLifetime valore diverso al parametro opzionale di. lifetime AddAWSService

Riutilizzare il client non significa smettere di eliminare le risposte successive all'operazione. Riutilizzate il client per tutta la durata dell'applicazione, ma continuate a eliminare le risposte e i flussi restituiti dalle singole operazioni, come descritto in. Elimina le risposte e gli stream

Elimina le risposte e gli stream per liberare le connessioni

Alcuni oggetti di risposta SDK trasmettono un flusso di rete in tempo reale. L'esempio più comune è GetObjectResponse, che implementa IDisposable ed espone il contenuto dell'oggetto tramite la sua proprietà. ResponseStream La risposta mantiene una connessione HTTP aperta finché lo stream non viene letto completamente o la risposta non viene eliminata. Se si diffondono queste risposte (ad esempio, richiamando GetObject un ciclo senza eliminare ogni risultato), le connessioni aperte si accumulano fino all'esaurimento del pool di connessioni e la chiamata successiva viene bloccata. Questa è la causa delle segnalazioni di download che «si bloccano casualmente» o si bloccano sull'ennesimo oggetto.

Racchiudi sempre una risposta in streaming in una using dichiarazione e leggi o copia il relativo stream prontamente.

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);

L'eliminazione della risposta e la sua eliminazione ResponseStream sono equivalenti; entrambe chiudono il flusso di rete sottostante e restituiscono la connessione al pool.

Suggerimento

Se ti servono solo i metadati degli oggetti, come dimensione, ora dell'ultima modifica, tipo di contenuto o ETag, chiama or invece di. GetObjectMetadata GetObjectMetadataAsync GetObject L'operazione sui metadati invia una HEAD richiesta HTTP e non trasferisce il corpo dell'oggetto, quindi non c'è alcun flusso di contenuti da gestire.

var metadata = await s3Client.GetObjectMetadataAsync(bucketName, key); Console.WriteLine($"Size: {metadata.ContentLength} bytes");
avvertimento

I tipi di risposta e stream, ad esempio, GetObjectResponse non implementano un finalizzatore, quindi non puoi fare affidamento su Garbage Collection per rilasciare le loro connessioni al posto tuo. Le risposte e gli stream devono essere eliminati in modo deterministico con o con una chiamata esplicita. using Dispose

Usa correttamente async/await

Nel moderno .NET, le operazioni di servizio in AWS SDK per .NET sono asincrone e restituiscono un. Task In .NET Framework esistono anche metodi sincroni, ma le chiamate asincrone hanno una scalabilità migliore e sono consigliate. Completa le operazioni con await e propagati async attraverso ogni livello del codice fino al punto di ingresso. Per ulteriori informazioni sulla programmazione asincrona con l'SDK, consulta. Programmazione asincrona

avvertimento

Non bloccare una chiamata SDK asincrona con,, o. .Result .Wait() .GetAwaiter().GetResult() Questo pattern di sincronizzazione su asincrono è una causa comune delle applicazioni che si bloccano:

  • In caso di caricamento, il blocco delle chiamate consuma i thread più velocemente di quanto possa crescere il pool di thread, quindi le continuazioni non possono essere eseguite. Questa carenza di thread-pool appare come un blocco a tempo indeterminato ed è la modalità di errore dominante nel moderno .NET (dove non è presente alcun valore predefinito). ASP.NET Core SynchronizationContext

  • Alcuni contesti acquisiscono un formatoSynchronizationContext, ad esempio le applicazioni classiche,, e .NET ASP.NET Windows Forms FrameworkWPF. Blazor WebAssembly In questi contesti, il blocco del thread chiamante mentre una continuazione richiede lo stesso thread produce un deadlock. Le operazioni nell'SDK vengono utilizzate ConfigureAwait(false) internamente, quindi non inviano le loro continuazioni nel contesto acquisito. La situazione di stallo deriva da un async codice presente in un'altra parte della catena di chiamate che lo rileva. L'utilizzo di chiamate SDK con await tutto il sistema evita completamente il problema.

Il metodo seguente blocca la chiamata asincrona e può bloccare o interrompere il pool di thread.

// Anti-pattern: do not do this. public GetObjectResponse Get(GetObjectRequest request) { return s3Client.GetObjectAsync(request).Result; }

Effettua invece il metodo e la chiamata. async await

public async Task<GetObjectResponse> GetAsync(GetObjectRequest request) { return await s3Client.GetObjectAsync(request); }

Raccomandazioni aggiuntive per il codice asincrono:

  • Effettua una CancellationToken risposta a ogni operazione in modo da poter annullare una chiamata lenta o bloccata anziché interromperla. Per ulteriori informazioni, consulta Utilizzo del CancellationToken parametro per i timeout.

  • Non inserire eccezioni in un blocco vuoto. catch In questo modo si nasconde il vero fallimento e si rende un blocco indistinguibile da un errore. Rileva le eccezioni specifiche e registrale.

  • Quando si genera nuovamente un'eccezione rilevata, throw; usatela throw ex; in modo da preservare la traccia dello stack originale.

  • Se dovete effettuare una chiamata da un limite sincrono, trattatela come ultima risorsa e isolate il lavoro dal contesto catturato anziché impostare il blocco delle chiamate come modello predefinito.

Configura la garbage collection .NET per AWS Lambda e Amazon ECS

Quando un contenitore sembra avere una «perdita di memoria», il garbage collector (GC) .NET potrebbe conservare la memoria recuperata per poterla riutilizzare. Di conseguenza, la memoria di processo può apparire elevata e stabile anche quando l'heap gestito non aumenta. Inoltre, il GC non rileva automaticamente il limite di memoria di un contenitore (il relativo limite cgroup). In un ambiente vincolato, l'heap potrebbe quindi aumentare verso la memoria dell'host anziché il limite del contenitore. Ciò può comportare la chiusura OutOfMemoryException o la chiusura del contenitore.

Per aiutare il GC a funzionare bene in ambienti limitati:

  • Imposta un limite di memoria esplicito sul contenitore in modo che il GC rispetti il limite di cgroup, and/or imposta la DOTNET_GCHeapHardLimit (un valore di byte assoluto, in esadecimale) o la variabile di ambiente per limitare l'heap gestito. DOTNET_GCHeapHardLimitPercent In diversi casi segnalati di esaurimento della memoria di Amazon ECS, l'impostazione di un limite di memoria rigida ha risolto i crash.

  • Sugli host small AWS Lambda e Amazon ECS, valuta la possibilità di disabilitare la garbage collection simultanea (in background) in modo che il raccoglitore non riservi memoria aggiuntiva; ad esempio, imposta la variabile di ambiente su o impostala nel DOTNET_gcConcurrent file di progetto. 0 <ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>

  • Limita la tua concorrenza. L'avvio di più operazioni contemporaneamente, ad esempio richiamare una raccolta Task.WhenAll di grandi dimensioni, aumenta la memoria del processo e può compromettere il pool di connessioni. Limita invece il grado di parallelismo. Ad esempio, evita questo schema illimitato:

    // Anti-pattern: starts one task per item with no limit. await Task.WhenAll(keys.Select(key => s3Client.GetObjectMetadataAsync(bucket, key)));

    Invece, limita la concorrenza con: Parallel.ForEachAsync

    var options = new ParallelOptions { MaxDegreeOfParallelism = 10 }; await Parallel.ForEachAsync(keys, options, async (key, token) => { await s3Client.GetObjectMetadataAsync(bucket, key, token); });

Per ulteriori informazioni su queste impostazioni, vedi Opzioni di configurazione del runtime per la garbage collection su learn.microsoft.com. Per indicazioni specifiche sull' AWS elaborazione, consulta il post sul blog di AWS Developer Tools Configuring .NET Garbage Collection for Amazon ECS e. AWS Lambda

Gestisci le connessioni HTTP e i limiti di connessione

In condizioni di throughput elevato, sono comuni due problemi relativi alla connessione. Il primo è l'apertura di troppe connessioni di breve durata. Ciò esaurisce le porte temporanee, lascia spazio ai socket e aumenta la latenza dell'handshake TCP TIME_WAIT e TLS. Il secondo è il numero insufficiente di connessioni disponibili, il che limita il parallelismo. Il riutilizzo di un singolo client di lunga durata (vediRiutilizza i client di servizio) è la base per un pool di connessioni sano, perché il pooling dipende dal client che viene riutilizzato.

Per regolare il numero di connessioni simultanee per endpoint, imposta la proprietà sulla configurazione del client. MaxConnectionsPerServer Quando questa proprietà è null (impostazione predefinita), si applica l'HttpClientHandlerimpostazione predefinita sottostante, che è effettivamente illimitata nel moderno .NET. Aumentalo solo quando molte richieste simultanee allo stesso endpoint hanno un collo di bottiglia nelle connessioni. Un buon punto di partenza è il numero massimo di richieste simultanee previste per endpoint. Impostarlo su un valore molto più elevato rispetto alle esigenze del carico di lavoro comporta uno spreco di socket senza migliorare la produttività.

using Amazon.S3; var config = new AmazonS3Config { MaxConnectionsPerServer = 50 }; var s3Client = new AmazonS3Client(config);

Se utilizzi già l'iniezione delle dipendenze, configura i tuoi clienti tramite. AWSSDK.Extensions.NETCore.Setup Questo è l'approccio consigliato quando si utilizza DI o si registrano diversi client di servizio. Centralizza la configurazione e semplifica l'iniezione e il test dei client. È possibile impostare i valori di configurazione dalla configurazione dell'applicazione anziché nel codice. Per ulteriori informazioni, consulta AWSSDK.Extensions.NETCore.Setup e iConfiguration.

Infine, limita il tuo parallelismo in modo da non avviare più operazioni simultanee di quelle consentite dal limite di connessione. Ad esempio, gate call con una SemaphoreSlim dimensione pari al limite di connessione:

var throttle = new SemaphoreSlim(50); // match MaxConnectionsPerServer await throttle.WaitAsync(token); try { await s3Client.GetObjectAsync(request, token); } finally { throttle.Release(); }

Configura timeout e tentativi

I timeout e i tentativi hanno un effetto diretto sulle prestazioni percepite. Un Timeout valore troppo alto consente a una richiesta bloccata per un lungo periodo di tempo. Quando un servizio sta già restituendo errori di limitazione, una politica di ripetizione aggressiva aggiunge più richieste e può peggiorare la limitazione. Scegliete una politica di ripetizione che soddisfi la tolleranza della vostra applicazione per quanto riguarda la latenza rispetto agli errori e lasciate che le eccezioni reali si propaghino invece di riprovare in modo da mascherare un blocco.

Nota

La Timeout proprietà non influisce sulle chiamate asincrone. Se si utilizzano chiamate asincrone, vedere invece. Utilizzo del CancellationToken parametro per i timeout

Per ulteriori informazioni sulle modalità di riprova e sulla Timeout proprietà (ReadWriteTimeoutsi applica solo a.NET Framework), insieme ad esempi su come impostarle, vedere. MaxErrorRetry Ritentativi e timeout

Ottimizza lo streaming e i trasferimenti di oggetti di grandi dimensioni (Amazon S3)

Per caricare e scaricare oggetti di grandi dimensioni o molti oggetti, utilizzate la TransferUtility classe nel Amazon.S3.Transfer namespace. Carica e scarica in parallelo utilizzando trasferimenti in più parti e gestisce flussi, parti e connessioni per te. È più veloce di un trasferimento a flusso singolo ed è il metodo consigliato per spostare oggetti di grandi dimensioni.

  • Download parallelo in più parti. Le versioni precedenti dell'SDK scaricavano un oggetto come flusso singolo, anziché scaricarne parti in parallelo. A partire dalla AWSSDK.S3 versione 4.0.17, TransferUtility fornisce il download in più parti (parallelo) tramite i metodiDownloadWithResponseAsync, eOpenStreamWithResponseAsync. DownloadDirectoryWithResponseAsync Quando si utilizzaOpenStreamWithResponseAsync, le parti dell'oggetto vengono memorizzate nel buffer mentre si consuma il flusso restituito. Controlla quante parti vengono bufferizzate con la MaxInMemoryParts proprietà di. TransferUtilityOpenStreamRequest Per la maggior parte dei trasferimenti, preferisciTransferUtility. Scarica tu stesso gli intervalli di byte con la ByteRange proprietà di GetObjectRequest solo quando hai bisogno di un intervallo specifico o di uno schema di parallelismo personalizzato. Per ulteriori informazioni, consulta Introduzione al supporto per il download in più parti per AWS SDK for .NET Transfer Manager nel Developer Tools Blog. AWS

  • Lunghezza dei contenuti da caricare. Un Amazon S3 PUT richiede una lunghezza del contenuto nota e, per impostazione predefinita, l'SDK calcola un checksum sul corpo della richiesta. Quando la lunghezza è nota e lo stream è ricercabile, l'SDK può farlo senza bufferizzare l'intero oggetto in memoria. TransferUtilityGestisce automaticamente gli stream non ricercabili mediante il buffering, se necessario. Se invece chiami PutObjectAsync direttamente con uno stream non ricercabile (ad esempio il corpo di una richiesta non elaborato), la richiesta può fallire. ASP.NET Core Fornisci uno stream ricercabile, imposta la lunghezza del contenuto in modo esplicito o configura il modo in cui l'SDK calcola i checksum. Per ulteriori informazioni, consulta la sezione Protezione dell'integrità dei dati nella Guida di riferimento di SDK e strumenti. AWS

  • Dimensionamento delle parti. Amazon S3 consente un massimo di 10.000 parti per caricamento multiparte. Quando la lunghezza totale è nota, l'SDK calcola automaticamente una dimensione della parte che rimane entro questo limite. Devi principalmente PartSize impostarti per stream la cui lunghezza non è nota in anticipo. Altrimenti, le parti predefinite di piccole dimensioni possono superare il limite per un caricamento molto grande. Una parte di dimensioni maggiori riduce inoltre il sovraccarico per parte, al costo di una maggiore memoria per parte.

Diagnostica problemi di prestazioni

Quando analizzi un rallentamento, un blocco o una perdita apparente, i seguenti segnali ti aiutano a individuare rapidamente la causa:

  • Un numero crescente di socket nello TIME_WAIT stato CLOSE_WAIT or (visibili connetstat) è l'impronta digitale delle risposte non disposte o dei clienti creati e scartati a ogni richiesta. Consulta Elimina le risposte e gli stream e Riutilizza i client di servizio.

  • Abilita le metriche delle richieste e la registrazione delle risposte per confermare che le richieste vengano effettivamente inviate e per misurare la latenza. Imposta le proprietà dell'LoggingConfigoggetto su AWSConfigs prima di creare i tuoi client di servizio. Un client acquisisce impostazioni, ad esempio LogMetrics quando viene creato.

    using Amazon; AWSConfigs.LoggingConfig.LogMetrics = true; AWSConfigs.LoggingConfig.LogResponses = ResponseLoggingOption.OnError;
  • Quando esamini la memoria, distingui la memoria dell'intero processo dall'heap gestito. La memoria di processo elevata ma stabile è spesso la memoria recuperata dal GC piuttosto che una perdita. Consulta Configura la raccolta dei rifiuti.