View a markdown version of this page

Mejores prácticas de rendimiento para AWS SDK para .NET - AWS SDK para .NET (V4)

¡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.

Orange button with text "Click here for details".

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:

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 aplicacionesSynchronizationContext, 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 utilizan ConfigureAwait(false) internamente, por lo que no publican sus continuaciones en el contexto capturado. El punto muerto se debe a un async código ubicado en otra parte de la cadena de llamadas que sí lo captura. Consumir llamadas al SDK de forma continua await evita 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 CancellationToken a 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 catch bloque 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 de throw 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 de DOTNET_GCHeapHardLimitPercent entorno 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_gcConcurrent entorno en el archivo del proyecto o 0 establé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.WhenAll En 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.ForEachAsync

    var 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 en learn.microsoft.com. Para obtener información específica sobre la AWS informática, consulte la entrada del blog de herramientas para AWS desarrolladores Cómo configurar la recolección de basura de.NET para Amazon ECS y. AWS Lambda

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.S3 la versión 4.0.17, TransferUtility permite la descarga en varias partes (en paralelo) mediante los métodos DownloadWithResponseAsync yOpenStreamWithResponseAsync. DownloadDirectoryWithResponseAsync Cuando 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. MaxInMemoryParts TransferUtilityOpenStreamRequest Para la mayoría de las transferencias, prefiera. TransferUtility Descargue los rangos de bytes usted mismo con la ByteRange propiedad 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 Manager en el AWS blog de herramientas para desarrolladores.

  • Longitud del contenido para subirlo. Un Amazon S3 PUT requiere 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, llamas PutObjectAsync directamente 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 PartSize prepararte 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_WAIT estado «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, LogMetrics cuando 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.