¡Ya está disponible la AWS SDK para .NET versión 4 (V4) del!
Para obtener información sobre los cambios importantes y la migración de sus aplicaciones, consulte el tema sobre https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/net-dg-v4.html migración.
Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.
Mejores prácticas de rendimiento para AWS SDK para .NET
La forma de crear clientes, gestionar las respuestas y configurar la aplicación tiene un gran efecto en el rendimiento, la latencia y el uso de la memoria. En este tema se describen los patrones de uso y configuración que ayudan a que las aplicaciones se ejecuten de manera eficiente y confiable. Estos patrones son más importantes en entornos con cargas elevadas o con recursos limitados, como los contenedores y las funciones sin servidor. Seguir estas prácticas puede mejorar el rendimiento y evitar problemas comunes, como la lentitud de las respuestas, los bloqueos y el uso excesivo de la memoria.
Las prácticas más impactantes son las siguientes:
-
Reutilice un único cliente de servicio duradero en lugar de crear uno por solicitud.
-
Elimine las respuestas y las transmisiones para que las conexiones de red se devuelvan al grupo de conexiones.
-
Utiliceasync/awaitcorrectamente y nunca bloquee las llamadas asincrónicas al SDK.
-
Configure la recolección de basura de.NET para entornos restringidos, como Amazon ECS. AWS Lambda
-
Administre las conexiones HTTP y los límites de conexión en condiciones de alto rendimiento.
Antes de empezar, asegúrese de haber configurado el entorno y el proyecto.
Reutilice un único cliente de servicio duradero
Los clientes de servicio, como AmazonS3Client, AmazonDynamoDBClient son seguros para subprocesos, su construcción es relativamente cara y tiene una larga vida útil. La creación de un cliente resuelve la información de la región y el punto final y establece la infraestructura HTTP subyacente. Cree un cliente por servicio y reutilícelo durante toda la vida útil de su aplicación. Si llamas a más de una región, crea un cliente independiente para cada región.
aviso
No cree un nuevo cliente de servicio para cada solicitud o dentro de un bucle. La creación de clientes interrumpe repetidamente la infraestructura HTTP subyacente, lo que añade una latencia mensurable. También puede agotar los sockets o identificadores y provocar que las solicitudes fallen o se bloqueen debido a la carga.
En las aplicaciones que utilizan la inyección de dependencias, registre el cliente como singleton. El método de AddAWSService extensión del AWSSDK.Extensions.NETCore.Setup NuGet paquete registra al cliente con una vida útil predeterminada deServiceLifetime.Singleton. El cliente se crea la primera vez que se solicita y la misma instancia se reutiliza durante todo el proceso. Para obtener más información sobre el registro de AWS
servicios mediante la inyección de dependencias y la lectura de las opciones de configuración, consulteAWSSDK.Extensions.NETCore.Setup e iConfiguration.
También puede registrar un cliente como singleton de forma manual.
builder.Services.AddSingleton<IAmazonS3>(_ => new AmazonS3Client());
nota
Como el cliente se AddAWSService registra como singleton de forma predeterminada, no deseche el cliente que proporciona. Si necesita una vida útil no predeterminada, pase un ServiceLifetime valor diferente al lifetime parámetro opcional de. AddAWSService
Reutilizar el cliente no significa que debas dejar de eliminar las respuestas por operación. Reutilice el cliente durante toda la vida útil de la aplicación, pero siga desechando las respuestas y los flujos que devuelven las operaciones individuales, tal y como se describe en. Deseche las respuestas y las transmisiones
Elimine las respuestas y las transmisiones para liberar las conexiones
Algunos objetos de respuesta del SDK transmiten una transmisión de red en vivo. El ejemplo más común es GetObjectResponse el que implementa IDisposable y expone el contenido del objeto a través de su ResponseStream propiedad. La respuesta mantiene una conexión HTTP abierta hasta que la transmisión se lea por completo o hasta que se deseche la respuesta. Si filtras estas respuestas (por ejemplo, al realizar una llamada GetObject en bucle sin eliminar cada resultado), las conexiones abiertas se acumulan hasta que se agota el grupo de conexiones y se bloquea la siguiente llamada. Esta es la causa de los informes de descargas que «se cuelgan al azar» o se detienen en el enésimo objeto.
Incluya siempre una respuesta de transmisión en una using declaración y lea o copie la transmisión con prontitud.
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);
Eliminar la respuesta y eliminar la suya ResponseStream son equivalentes; cualquiera de las dos cierra la transmisión de red subyacente y devuelve la conexión al grupo.
sugerencia
Si solo necesitas los metadatos del objeto, como el tamaño, la hora de la última modificación, el tipo de contenido o la ETag, llama o en lugar de. GetObjectMetadata GetObjectMetadataAsync GetObject La operación de metadatos emite una HEAD solicitud HTTP y no transfiere ningún cuerpo del objeto, por lo que no hay que gestionar ningún flujo de contenido.
var metadata = await s3Client.GetObjectMetadataAsync(bucketName, key); Console.WriteLine($"Size: {metadata.ContentLength} bytes");
aviso
Los tipos de respuesta y transmisión, por ejemplo, GetObjectResponse no implementan un finalizador, por lo que no puedes confiar en la recolección de basura para liberar sus conexiones. Debes eliminar las respuestas y las transmisiones de forma determinista, con una llamada using o una llamada explícita. Dispose
Úsalo correctamente async/await
En la versión moderna de .NET, las operaciones de servicio AWS SDK para .NET son asincrónicas y devuelven un. Task En .NET Framework, también existen métodos sincrónicos, pero las llamadas asincrónicas se escalan mejor y se recomiendan. Consuma operaciones con await todas las capas del código y async propágalas a través de ellas hasta el punto de entrada. Para obtener más información sobre la programación asincrónica con el SDK, consulte. Programación asíncrona
aviso
No bloquee una llamada asincrónica del SDK con, o. .Result .Wait() .GetAwaiter().GetResult() Este patrón de sincronización mediante sincronización asíncrona es una causa frecuente de que las aplicaciones se bloqueen:
-
Bajo carga, el bloqueo de llamadas consume subprocesos más rápido de lo que puede crecer el grupo de subprocesos, por lo que las continuaciones no se pueden ejecutar. Esta falta de subprocesos aparece como un bloqueo indefinido y es el modo de fallo dominante en el sistema .NET moderno (donde ASP.NET Core no existe ningún modo predeterminado).
SynchronizationContext -
Algunos contextos capturan aplicaciones
SynchronizationContext, como las clásicasASP.NET, Windows Forms WPFBlazor WebAssembly, y .NET Framework. En estos contextos, bloquear el subproceso de llamada mientras una continuación necesita ese mismo subproceso produce un punto muerto. Las operaciones del SDK se utilizanConfigureAwait(false)internamente, por lo que no publican sus continuaciones en el contexto capturado. El punto muerto se debe a unasynccódigo ubicado en otra parte de la cadena de llamadas que sí lo captura. Consumir llamadas al SDK de forma continuaawaitevita el problema por completo.
El siguiente método bloquea la llamada asincrónica y puede bloquear o privar al grupo de subprocesos.
// Anti-pattern: do not do this. public GetObjectResponse Get(GetObjectRequest request) { return s3Client.GetObjectAsync(request).Result; }
En su lugar, crea el método y la llamada. async await
public async Task<GetObjectResponse> GetAsync(GetObjectRequest request) { return await s3Client.GetObjectAsync(request); }
Recomendaciones adicionales para el código asíncrono:
-
Pase una
CancellationTokena cada operación para poder cancelar una llamada lenta o detenida en lugar de suspenderse. Para obtener más información, consulte Uso del parámetro CancellationToken para los tiempos de espera. -
No te tragues las excepciones en un
catchbloque vacío. Hacerlo oculta el verdadero fracaso y hace que un bloqueo sea indistinguible de un error. Detecta excepciones específicas y regístralas. -
Al volver a lanzar una excepción detectada,
throw;utilícela en lugar dethrow ex;hacerlo para conservar el rastro original de la pila. -
Si tienes que llamar desde un límite sincrónico, trátalo como último recurso y aísla la obra del contexto capturado en lugar de convertir las llamadas de bloqueo en el patrón predeterminado.
Configure la recolección de basura de.NET para AWS Lambda y Amazon ECS
Cuando un contenedor parece tener una «pérdida de memoria», es posible que el recolector de basura (GC) .NET retenga la memoria recuperada para reutilizarla. Como resultado, la memoria de los procesos puede parecer alta y estable incluso cuando la pila gestionada no crece. Además, el GC no detecta automáticamente el límite de memoria de un contenedor (su límite de grupos). En un entorno limitado, la pila podría crecer hasta alcanzar la memoria del host en lugar del límite del contenedor. Esto puede provocar el cierre del contenedor OutOfMemoryException o su cierre.
Para ayudar a que el GC funcione bien en entornos limitados:
-
Establezca un límite de memoria explícito en el contenedor para que el GC respete el límite de cgroup, and/or defina la variable de entorno
DOTNET_GCHeapHardLimit(un valor de byte absoluto, en hexadecimal) o la variable deDOTNET_GCHeapHardLimitPercententorno para limitar el montón administrado. En varios casos denunciados de falta de memoria en Amazon ECS, el establecimiento de un límite de memoria fija resolvió los bloqueos. -
En los hosts pequeños AWS Lambda y de Amazon ECS, considere la posibilidad de deshabilitar la recolección de basura simultánea (en segundo plano) para que el recopilador no reserve memoria adicional; por ejemplo, defina la variable de
DOTNET_gcConcurrententorno en el archivo del proyecto o0establézcala<ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>en él. -
Limita tu simultaneidad. Lanzar muchas operaciones a la vez, como recuperar una colección grande, infla la memoria de los procesos y puede agotar el pool de conexiones.
Task.WhenAllEn su lugar, limite el grado de paralelismo. Por ejemplo, evita este patrón ilimitado:// Anti-pattern: starts one task per item with no limit. await Task.WhenAll(keys.Select(key => s3Client.GetObjectMetadataAsync(bucket, key)));En su lugar, limita la concurrencia con:
Parallel.ForEachAsyncvar options = new ParallelOptions { MaxDegreeOfParallelism = 10 }; await Parallel.ForEachAsync(keys, options, async (key, token) => { await s3Client.GetObjectMetadataAsync(bucket, key, token); });
Para obtener más información acerca de estas opciones, consulte Opciones de configuración en tiempo de ejecución para la recolección de basura
Gestione las conexiones HTTP y los límites de conexión
En condiciones de alto rendimiento, son frecuentes dos problemas relacionados con la conexión. El primero es abrir demasiadas conexiones de corta duración. Esto agota los puertos efímeros, deja los sockets conectados y añade la latencia del TIME_WAIT protocolo de enlace TCP y TLS. La segunda es tener muy pocas conexiones disponibles, lo que dificulta el paralelismo. La reutilización de un único cliente de larga duración (consulteReutilice los clientes de servicio) es la base de una agrupación de conexiones en buen estado, ya que la agrupación depende del cliente que se esté reutilizando.
Para ajustar el número de conexiones simultáneas por punto final, defina la propiedad en la configuración del MaxConnectionsPerServer cliente. Cuando esta propiedad es null (la predeterminada), se aplica la HttpClientHandler predeterminada subyacente, que de hecho es ilimitada en el sistema .NET moderno. Auméntela solo cuando muchas solicitudes simultáneas dirigidas al mismo punto final provoquen un embotellamiento en las conexiones. Un buen punto de partida es el número máximo de solicitudes simultáneas que se espera por punto final. Si se establece un valor mucho mayor que el que necesita su carga de trabajo, se desperdician sockets sin mejorar el rendimiento.
using Amazon.S3; var config = new AmazonS3Config { MaxConnectionsPerServer = 50 }; var s3Client = new AmazonS3Client(config);
Si ya usa la inyección de dependencias, configure sus clientes medianteAWSSDK.Extensions.NETCore.Setup. Este es el enfoque recomendado cuando se utiliza DI o se registran varios clientes de servicio. Centraliza la configuración y hace que los clientes sean fáciles de inyectar y probar. Puede establecer los valores de configuración a partir de la configuración de su aplicación en lugar de hacerlo desde el código. Para obtener más información, consulte AWSSDK.Extensions.NETCore.Setup e iConfiguration.
Por último, delimita tu propio paralelismo para no iniciar más operaciones simultáneas de las que permite tu límite de conexión. Por ejemplo, cancela las llamadas con un SemaphoreSlim tamaño igual al límite de tu conexión:
var throttle = new SemaphoreSlim(50); // match MaxConnectionsPerServer await throttle.WaitAsync(token); try { await s3Client.GetObjectAsync(request, token); } finally { throttle.Release(); }
Configure los tiempos de espera y los reintentos
Los tiempos de espera y los reintentos tienen un efecto directo en el rendimiento percibido. Un Timeout valor demasiado alto permite que una solicitud estancada se bloquee durante mucho tiempo. Cuando un servicio ya está produciendo errores de limitación, una política de reintentos agresiva añade más solicitudes y puede empeorar la limitación. Elige una política de reintentos que se ajuste a la tolerancia de tu aplicación con respecto a la latencia frente a los errores, y deja que las excepciones genuinas se propaguen en lugar de volver a intentarlo de forma que se oculte un bloqueo.
nota
La Timeout propiedad no afecta a las llamadas asincrónicas. Si utiliza llamadas asincrónicas, consulte en su lugar. Uso del parámetro CancellationToken para los tiempos de espera
Para obtener más información acerca de los modos de reintento y la Timeout propiedad (ReadWriteTimeoutse aplica únicamente a .NET Framework), junto con ejemplos de cómo configurarlos, consulte. MaxErrorRetry Reintentos y tiempos de espera
Optimice la transmisión y las transferencias de objetos grandes (Amazon S3)
Para cargar y descargar objetos grandes o muchos objetos, usa la TransferUtility clase del espacio de Amazon.S3.Transfer nombres. Se carga y descarga en paralelo mediante transferencias multiparte y gestiona automáticamente las transmisiones, las partes y las conexiones. Es más rápido que una transferencia de un solo flujo y es la forma recomendada de mover objetos grandes.
-
Descarga multiparte en paralelo. Las versiones anteriores del SDK descargaban un objeto como una secuencia única, en lugar de descargar partes en paralelo. A partir de
AWSSDK.S3la versión 4.0.17,TransferUtilitypermite la descarga en varias partes (en paralelo) mediante los métodosDownloadWithResponseAsyncyOpenStreamWithResponseAsync.DownloadDirectoryWithResponseAsyncCuando los usasOpenStreamWithResponseAsync, las partes del objeto se almacenan en búfer en la memoria mientras consumes la secuencia devuelta. Controla el número de partes que se almacenan en búfer con la propiedad de.MaxInMemoryPartsTransferUtilityOpenStreamRequest Para la mayoría de las transferencias, prefiera.TransferUtilityDescargue los rangos de bytes usted mismo con laByteRangepropiedad de GetObjectRequest solo cuando necesite un rango específico o un esquema de paralelismo personalizado. Para obtener más información, consulte Introducción a la compatibilidad con la descarga multiparte del AWS SDK para .NET Transfer Manageren el AWS blog de herramientas para desarrolladores. -
Longitud del contenido para subirlo. Un Amazon S3
PUTrequiere una longitud de contenido conocida y, de forma predeterminada, el SDK calcula una suma de comprobación sobre el cuerpo de la solicitud. Cuando se conoce la longitud y se puede buscar la transmisión, el SDK puede hacerlo sin almacenar en búfer todo el objeto en la memoria.TransferUtilityGestiona automáticamente las transmisiones que no se pueden buscar almacenándolas en búfer según sea necesario. Si, en cambio, llamasPutObjectAsyncdirectamente con una secuencia que no se puede buscar (por ejemplo, el cuerpo de una solicitud sin procesar), la solicitud puede fallar. ASP.NET Core Proporciona una transmisión que se pueda buscar, establece la longitud del contenido de forma explícita o configura la forma en que el SDK calcula las sumas de comprobación. Para obtener más información, consulta las protecciones de la integridad de los datos en la Guía de referencia de herramientas y AWS SDK. -
Dimensionamiento de piezas. Amazon S3 permite un máximo de 10 000 partes por carga multiparte. Cuando se conoce la longitud total, el SDK calcula automáticamente un tamaño de pieza que se mantiene dentro de este límite. Principalmente, tienes que
PartSizeprepararte para las transmisiones cuya longitud no se conozca de antemano. De lo contrario, las pequeñas partes predeterminadas pueden superar el límite en una subida muy grande. Un tamaño de pieza más grande también reduce la sobrecarga por pieza, a costa de más memoria por pieza.
Diagnostique problemas de rendimiento
Cuando investigue una ralentización, un bloqueo o una aparente fuga, las siguientes señales le ayudarán a encontrar rápidamente la causa:
-
El número cada vez mayor de sockets en
TIME_WAITestado «CLOSE_WAITo» (visibles connetstat) es la huella digital de las respuestas no resueltas o de los clientes que se crean y descartan por solicitud. Consulte Deseche las respuestas y las transmisiones y Reutilice los clientes de servicio. -
Activa las métricas de solicitudes y el registro de respuestas para confirmar que las solicitudes se están enviando realmente y para medir la latencia. Establezca las propiedades del LoggingConfig objeto en AWSConfig antes de crear sus clientes de servicio. Un cliente captura la configuración, por ejemplo,
LogMetricscuando se construye.using Amazon; AWSConfigs.LoggingConfig.LogMetrics = true; AWSConfigs.LoggingConfig.LogResponses = ResponseLoggingOption.OnError; -
Al investigar la memoria, distinga la memoria que abarca todo el proceso de la pila gestionada. La memoria de proceso alta pero estable suele ser el GC que contiene la memoria recuperada en lugar de una pérdida. Consulte Configure la recolección de basura.