Usar la programación asíncrona - AWS SDK for Java 2.x

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.

Usar la programación asíncrona

AWS SDK for Java 2.x Cuenta con clientes asíncronos con soporte de E/S sin bloqueo que implementan una alta concurrencia en unos pocos subprocesos. Sin embargo, no se garantiza una E/S total sin bloqueo. Un cliente asíncrono puede bloquear las llamadas en algunos casos, como la recuperación de credenciales, la firma de solicitudes mediante la versión 4 de AWS Signature (SiGv4) o la detección de terminales.

Los métodos síncronos bloquean la ejecución de los subprocesos hasta que el cliente recibe una respuesta del servicio. Los métodos asíncronos terminan de ejecutarse inmediatamente, devolviendo el control al subproceso que realiza la llamada sin esperar una respuesta.

Como un método asíncrono termina de ejecutarse antes de que haya una respuesta disponible, necesita una forma de obtener la respuesta cuando esté lista. Los métodos para el cliente asíncrono de la versión 2.x de los CompletableFuture objetos AWS SDK for Java devuelven objetos que permiten acceder a la respuesta cuando esté lista.

Usa un cliente asíncrono APIs

Las firmas de los métodos de cliente asíncronos son las mismas que las de los métodos sincrónicos, pero los métodos asíncronos devuelven un CompletableFutureobjeto que contiene los resultados de la operación asincrónica en el futuro. Si se produce un error mientras se ejecuta el método asíncrono, el SDK error se arroja como. CompletionException

Un enfoque que puede utilizar para obtener el resultado es encadenar un whenComplete() método al CompletableFuture devuelto por la llamada al método. SDK El whenComplete() método recibe el resultado o un objeto del tipo Throwable en CompletionException función de cómo se haya completado la llamada asincrónica. Debe proporcionar una acción para procesar o comprobar whenComplete() los resultados antes de devolverlos al código de llamada.

Si quieres devolver algo distinto del objeto devuelto por el SDK método, usa el handle() método en su lugar. El handle() método utiliza los mismos parámetros quewhenComplete(), pero puede procesar el resultado y devolver un objeto.

Para esperar a que se complete la cadena asíncrona y recuperar los resultados de la finalización, puede llamar al método. join() Si el Throwable objeto no estaba incluido en la cadena, el join() método muestra una casilla sin marcar CompletionException que contiene la excepción original. Se accede a la excepción original con. CompletionException#getCause() También puede utilizar el CompletableFuture#get() método para obtener los resultados de finalización. Sin embargo, el get() método puede generar excepciones comprobadas.

El siguiente ejemplo muestra dos variantes de cómo se puede trabajar con el listTables() método del cliente asíncrono de DynamoDB. La acción realizada whenComplete() simplemente registra una respuesta correcta, mientras que la handle() versión extrae la lista de nombres de tablas y la devuelve. En ambos casos, si se genera un error en la cadena asíncrona, el error se vuelve a generar para que el código del cliente pueda gestionarlo.

Importaciones

import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest; import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse; import java.util.List; import java.util.concurrent.CompletableFuture;

Código

whenComplete() variation
public class DynamoDbAsyncListTables { public static void main(String[] args) { Region region = Region.US_EAST_1; DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbAsyncClient.builder().region(region).build(); try { ListTablesResponse listTablesResponse = listTablesWhenComplete(dynamoDbAsyncClient).join(); // The join() method may throw a CompletionException. if (listTablesResponse.hasTableNames()){ System.out.println("Table exist in this region: " + region.id()); } } catch (RuntimeException e) { // Handle as needed. Here we simply print out the class names. System.out.println(e.getClass()); // Prints 'class java.util.concurrent.CompletionException'. System.out.println(e.getCause().getClass()); // Prints 'class software.amazon.awssdk.services.dynamodb.model.DynamoDbException'. } } public static CompletableFuture<ListTablesResponse> listTablesWhenComplete(DynamoDbAsyncClient client) { return client.listTables(ListTablesRequest.builder().build()) .whenComplete((listTablesResponse, throwable) -> { if (listTablesResponse != null) { // Consume the response. System.out.println("The SDK's listTables method completed successfully."); } else { RuntimeException cause = (RuntimeException) throwable.getCause(); // If an error was thrown during the SDK's listTables method it is wrapped in a CompletionException. // The SDK throws only RuntimeExceptions, so this is a safe cast. System.out.println(cause.getMessage()); // Log error here, but rethrow so the calling code can handle as needed. throw cause; } }); }
handle() variation
public class DynamoDbAsyncListTables { public static void main(String[] args) { Region region = Region.US_EAST_1; DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbAsyncClient.builder().region(region).build(); try { List<String> tableNames = listTablesHandle(dynamoDbAsyncClient).join(); // The join() method may throw a CompletionException. tableNames.forEach(System.out::println); } catch (RuntimeException e) { // Handle as needed. Here we simply print out the class names. System.out.println(e.getClass()); // Prints 'class java.util.concurrent.CompletionException'. System.out.println(e.getCause().getClass()); // Prints 'class software.amazon.awssdk.services.dynamodb.model.DynamoDbException'. } } public static CompletableFuture<List<String>> listTablesHandle(DynamoDbAsyncClient client) { return client.listTables(ListTablesRequest.builder().build()) .handle((listTablesResponse, throwable) -> { if (listTablesResponse != null) { return listTablesResponse.tableNames(); // Return the list of table names. } else { RuntimeException cause = (RuntimeException) throwable.getCause(); // If an error was thrown during the SDK's listTables method it is wrapped in a CompletionException. // The SDK throws only RuntimeExceptions, so this is a safe cast. System.out.println(cause.getMessage()); // Log error here, but rethrow so the calling code can handle as needed. throw cause; } }); } }

Gestione la transmisión en métodos asíncronos

En el caso de los métodos asíncronos con contenido en streaming, debes proporcionar un AsyncRequestBodypara proporcionar el contenido de forma incremental o uno para recibir y procesar la respuesta AsyncResponseTransformer.

En el siguiente ejemplo, se carga un archivo de forma asíncrona mediante la forma Amazon S3 asíncrona de la operación. PutObject

Importaciones

import software.amazon.awssdk.core.async.AsyncRequestBody; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3AsyncClient; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectResponse; import java.nio.file.Paths; import java.util.concurrent.CompletableFuture;

Código

/** * To run this AWS code example, ensure that you have setup your development environment, including your AWS credentials. * * For information, see this documentation topic: * * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html */ public class S3AsyncOps { public static void main(String[] args) { final String USAGE = "\n" + "Usage:\n" + " S3AsyncOps <bucketName> <key> <path>\n\n" + "Where:\n" + " bucketName - the name of the Amazon S3 bucket (for example, bucket1). \n\n" + " key - the name of the object (for example, book.pdf). \n" + " path - the local path to the file (for example, C:/AWS/book.pdf). \n" ; if (args.length != 3) { System.out.println(USAGE); System.exit(1); } String bucketName = args[0]; String key = args[1]; String path = args[2]; Region region = Region.US_WEST_2; S3AsyncClient client = S3AsyncClient.builder() .region(region) .build(); PutObjectRequest objectRequest = PutObjectRequest.builder() .bucket(bucketName) .key(key) .build(); // Put the object into the bucket CompletableFuture<PutObjectResponse> future = client.putObject(objectRequest, AsyncRequestBody.fromFile(Paths.get(path)) ); future.whenComplete((resp, err) -> { try { if (resp != null) { System.out.println("Object uploaded. Details: " + resp); } else { // Handle error err.printStackTrace(); } } finally { // Only close the client when you are completely done with it client.close(); } }); future.join(); } }

En el siguiente ejemplo, se obtiene un archivo Amazon S3 mediante la forma asíncrona de la operación. GetObject

Importaciones

import software.amazon.awssdk.core.async.AsyncResponseTransformer; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3AsyncClient; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.GetObjectResponse; import java.nio.file.Paths; import java.util.concurrent.CompletableFuture;

Código

/** * To run this AWS code example, ensure that you have setup your development environment, including your AWS credentials. * * For information, see this documentation topic: * * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html */ public class S3AsyncStreamOps { public static void main(String[] args) { final String USAGE = "\n" + "Usage:\n" + " S3AsyncStreamOps <bucketName> <objectKey> <path>\n\n" + "Where:\n" + " bucketName - the name of the Amazon S3 bucket (for example, bucket1). \n\n" + " objectKey - the name of the object (for example, book.pdf). \n" + " path - the local path to the file (for example, C:/AWS/book.pdf). \n" ; if (args.length != 3) { System.out.println(USAGE); System.exit(1); } String bucketName = args[0]; String objectKey = args[1]; String path = args[2]; Region region = Region.US_WEST_2; S3AsyncClient client = S3AsyncClient.builder() .region(region) .build(); GetObjectRequest objectRequest = GetObjectRequest.builder() .bucket(bucketName) .key(objectKey) .build(); CompletableFuture<GetObjectResponse> futureGet = client.getObject(objectRequest, AsyncResponseTransformer.toFile(Paths.get(path))); futureGet.whenComplete((resp, err) -> { try { if (resp != null) { System.out.println("Object downloaded. Details: "+resp); } else { err.printStackTrace(); } } finally { // Only close the client when you are completely done with it client.close(); } }); futureGet.join(); } }

Configure las opciones asíncronas avanzadas

La versión AWS SDK for Java 2.x utiliza Netty, un marco de aplicaciones de red asíncrono basado en eventos, para gestionar los subprocesos de E/S. El AWS SDK for Java 2.x crea un Netty ExecutorService subyacente para completar los futuros devueltos desde la solicitud del cliente hasta el cliente de Netty. HTTP Esta abstracción reduce el riesgo de que una aplicación interrumpa el proceso de sincronización si los desarrolladores deciden detener o suspender los subprocesos. De forma predeterminada, cada cliente asíncrono crea un grupo de subprocesos en función del número de procesadores y gestiona las tareas de una cola dentro del ExecutorService.

Puede especificar una JDK implementación específica para ExecutorService cuando cree su cliente asíncrono. El siguiente fragmento crea una ExecutorService con un número fijo de subprocesos.

Código

S3AsyncClient clientThread = S3AsyncClient.builder() .asyncConfiguration( b -> b.advancedOption(SdkAdvancedAsyncClientOption .FUTURE_COMPLETION_EXECUTOR, Executors.newFixedThreadPool(10) ) ) .build();

Para optimizar el rendimiento, puede administrar su propio ejecutor de grupo de subprocesos e incluirlo al configurar su cliente.

ThreadPoolExecutor executor = new ThreadPoolExecutor(50, 50, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(<custom_value>), new ThreadFactoryBuilder() .threadNamePrefix("sdk-async-response").build()); // Allow idle core threads to time out executor.allowCoreThreadTimeOut(true); S3AsyncClient clientThread = S3AsyncClient.builder() .asyncConfiguration( b -> b.advancedOption(SdkAdvancedAsyncClientOption .FUTURE_COMPLETION_EXECUTOR, executor ) ) .build();