Programmation de DynamoDB avec le kit AWS SDK for Java 2.x
Ce guide de programmation fournit une orientation aux programmeurs qui souhaitent utiliser Amazon DynamoDB avec Java. Le guide couvre différents concepts, notamment les couches d’abstraction, la gestion de la configuration, la gestion des erreurs, le contrôle des politiques de nouvelles tentatives et la gestion de keep-alive.
Rubriques
À propos de la AWS SDK for Java 2.x
Vous pouvez accéder à DynamoDB depuis Java en utilisant le kit AWS SDK pour Java officiel. Le kit SDK pour Java est disponible en deux versions : 1.x et 2.x. La fin du support de la version 1.x a été annoncée
Pour en savoir plus sur la maintenance et le support des kits AWS SDK, consultez Politique de maintenance des kits AWS SDK et des outils et Matrice de prise en charge des versions des kits AWS SDK et des outils dans le Guide de référence des kits AWS SDK et des outils.
Le kit AWS SDK for Java 2.x est une réécriture majeure du code base de la version 1.x. Le kit SDK pour Java 2.x prend en charge les fonctionnalités Java modernes, telles que les E/S non bloquantes introduites dans Java 8. Le kit SDK pour Java 2.x prend également en charge les implémentations de clients HTTP enfichables afin de fournir une plus grande flexibilité de connexion réseau et des options de configuration supplémentaires.
Un changement notable entre le kit SDK pour Java 1.x et le kit SDK pour Java 2.x est l’utilisation d’un nouveau nom de package. Le kit Java 1.x SDK utilise le nom du package com.amazonaws, tandis que le kit Java 2.x SDK utilise software.amazon.awssdk. De même, les artefacts Maven du kit SDK Java 1.x utilisent le groupId com.amazonaws, tandis que les artefacts du kit SDK Java 2.x utilisent le groupId software.amazon.awssdk.
Important
Le kit AWS SDK pour Java 1.x possède un package DynamoDB nommé com.amazonaws.dynamodbv2. Le « v2 » dans le nom du package n’indique pas qu’il s’agit de Java 2 (J2SE). « v2 » indique plutôt que le package prend en charge la seconde version de l’API de bas niveau DynamoDB au lieu de la version d’origine de l’API de bas niveau.
Support pour les versions de Java
Le kit AWS SDK for Java 2.x fournit un support complet pour les versions Java
Démarrer avec le kit AWS SDK for Java 2.x
Le didacticiel suivant montre comment utiliser Apache Maven
Pour terminer ce didacticiel, procédez comme suit :
Étape 1 : Configuration pour ce didacticiel
Avant de commencer ce didacticiel, vous aurez besoin des éléments suivants :
-
Autorisation d’accès à DynamoDB.
-
Un environnement de développement Java configuré avec un accès par authentification unique aux Services AWS avec le Portail d'accès AWS.
Pour procéder à la configuration pour ce didacticiel, suivez les instructions de Présentation de la configuration dans le Guide du développeur du kit AWS SDK for Java 2.x. Une fois que vous avez configuré votre environnement de développement avec un accès par authentification unique pour le kit Java SDK et que vous disposez d’une session de portail d’accès AWS active, passez à l’étape 2 de ce didacticiel.
Étape 2 : Création du projet
Pour créer le projet pour ce didacticiel, vous devez exécuter une commande Maven qui vous invite à saisir des informations sur la configuration du projet. Une fois toutes les entrées saisies et confirmées, Maven termine la création du projet en créant un fichier pom.xml et des fichiers Java stub.
-
Ouvrez un terminal ou une fenêtre d’invite de commande et naviguez jusqu’au répertoire de votre choix, par exemple, le dossier
DesktopouHome. -
Saisissez la commande suivante sur le terminal et appuyez sur Entrée.
mvn archetype:generate \ -DarchetypeGroupId=software.amazon.awssdk \ -DarchetypeArtifactId=archetype-app-quickstart \ -DarchetypeVersion=2.22.0 -
Pour chaque invite, saisissez la valeur indiquée dans la deuxième colonne.
Invite Valeur à saisir Define value for property 'service':dynamodbDefine value for property 'httpClient':apache-clientDefine value for property 'nativeImage':falseDefine value for property 'credentialProvider'identity-centerDefine value for property 'groupId':org.exampleDefine value for property 'artifactId':getstartedDefine value for property 'version' 1.0-SNAPSHOT:<Enter>Define value for property 'package' org.example:<Enter> -
Une fois que vous avez saisi la dernière valeur, Maven répertorie les choix que vous avez effectués. Pour confirmer, saisissez O. Vous pouvez également saisir N, puis saisir à nouveau vos choix.
Maven crée un dossier de projet nommé getstarted en fonction de la valeur artifactId que vous avez saisie. Dans le dossier getstarted, recherchez un fichier nommé README.md que vous pouvez consulter, un fichier pom.xml et un répertoire src.
Maven crée l’arborescence de répertoires suivante.
getstarted ├── README.md ├── pom.xml └── src ├── main │ ├── java │ │ └── org │ │ └── example │ │ ├── App.java │ │ ├── DependencyFactory.java │ │ └── Handler.java │ └── resources │ └── simplelogger.properties └── test └── java └── org └── example └── HandlerTest.java 10 directories, 7 files
L’exemple suivant affiche le contenu du fichier du projet pom.xml.
La section dependencyManagement contient une dépendance vis-à-vis du kit AWS SDK for Java 2.x, et la section dependencies possède une dépendance pour DynamoDB. La spécification de ces dépendances oblige Maven à inclure les fichiers .jar appropriés dans le chemin de la classe Java. Par défaut, le kit AWS SDK n’inclut pas toutes les classes pour tous les Services AWS. Pour DynamoDB, si vous utilisez l’interface de bas niveau, vous devez avoir une dépendance à l’égard de l’artefact dynamodb. Ou, si vous utilisez l’interface de haut niveau, sur l’artefact dynamodb-enhanced. Si vous n’incluez pas les dépendances pertinentes, le code ne peut pas être compilé. Le projet utilise Java 1.8 en raison de la valeur 1.8 des propriétés maven.compiler.source et maven.compiler.target.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>getstarted</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.shade.plugin.version>3.2.1</maven.shade.plugin.version> <maven.compiler.plugin.version>3.6.1</maven.compiler.plugin.version> <exec-maven-plugin.version>1.6.0</exec-maven-plugin.version> <aws.java.sdk.version>2.22.0</aws.java.sdk.version><-------- SDK version picked up from archetype version. <slf4j.version>1.7.28</slf4j.version> <junit5.version>5.8.1</junit5.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>bom</artifactId> <version>${aws.java.sdk.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>dynamodb</artifactId><-------- DynamoDB dependency<exclusions> <exclusion> <groupId>software.amazon.awssdk</groupId> <artifactId>netty-nio-client</artifactId> </exclusion> <exclusion> <groupId>software.amazon.awssdk</groupId> <artifactId>apache-client</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>sso</artifactId><-------- Required for identity center authentication.</dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>ssooidc</artifactId><-------- Required for identity center authentication.</dependency> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>apache-client</artifactId><-------- HTTP client specified.<exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>${slf4j.version}</version> </dependency> <!-- Needed to adapt Apache Commons Logging used by Apache HTTP Client to Slf4j to avoid ClassNotFoundException: org.apache.commons.logging.impl.LogFactoryImpl during runtime --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>${slf4j.version}</version> </dependency> <!-- Test Dependencies --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>${junit5.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>${maven.compiler.plugin.version}</version> </plugin> </plugins> </build> </project>
Étape 3 : Écriture du code
Le code suivant présente la classe App créée par Maven. La méthode main est le point d’entrée dans l’application, qui crée une instance de la classe Handler puis appelle sa méthode sendRequest.
package org.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class App { private static final Logger logger = LoggerFactory.getLogger(App.class); public static void main(String... args) { logger.info("Application starts"); Handler handler = new Handler(); handler.sendRequest(); logger.info("Application ends"); } }
La classe DependencyFactory créée par Maven contient la méthode d’usine dynamoDbClient qui construit et renvoie une instance DynamoDbClientDynamoDbClient utilise une instance du client HTTP basé sur Apache. Cela est dû au fait que vous avez spécifié apache-client quand Maven vous a invité à spécifier le client HTTP à utiliser.
Le code suivant présente la classe DependencyFactory.
package org.example; import software.amazon.awssdk.http.apache.ApacheHttpClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; /** * The module containing all dependencies required by the {@link Handler}. */ public class DependencyFactory { private DependencyFactory() {} /** * @return an instance of DynamoDbClient */ public static DynamoDbClient dynamoDbClient() { return DynamoDbClient.builder() .httpClientBuilder(ApacheHttpClient.builder()) .build(); } }
La classe Handler contient la logique principale de votre programme. Lorsqu’une instance de Handler est créée dans la classe App, la DependencyFactory fournit le client de service DynamoDbClient. Votre code utilise l’instance de DynamoDbClient pour appeler DynamoDB.
Maven génère la classe Handler suivante avec un commentaire TODO. L’étape suivante du didacticiel consiste à remplacer le commentaire TODO par du code.
package org.example; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; public class Handler { private final DynamoDbClient dynamoDbClient; public Handler() { dynamoDbClient = DependencyFactory.dynamoDbClient(); } public void sendRequest() { // TODO: invoking the API calls using dynamoDbClient. } }
Pour renseigner la logique, remplacez l’ensemble du contenu de la classe Handler par le code suivant. La méthode sendRequest est renseignée et les importations nécessaires sont ajoutées.
Le code suivant utilise l’instance DynamoDbClientLogger pour enregistrer les noms de ces tables.
package org.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse; public class Handler { private final DynamoDbClient dynamoDbClient; public Handler() { dynamoDbClient = DependencyFactory.dynamoDbClient(); } public void sendRequest() { Logger logger = LoggerFactory.getLogger(Handler.class); logger.info("calling the DynamoDB API to get a list of existing tables"); ListTablesResponse response = dynamoDbClient.listTables(); if (!response.hasTableNames()) { logger.info("No existing tables found for the configured account & region"); } else { response.tableNames().forEach(tableName -> logger.info("Table: " + tableName)); } } }
Étape 4 : Création et exécution de l’application
Une fois que vous avez créé le projet et qu’il contient la classe Handler complète, créez et exécutez l’application.
-
Vérifiez que vous disposez d’une session AWS IAM Identity Center active. Pour confirmer, exécutez la commande de l’AWS Command Line Interface (AWS CLI)
aws sts get-caller-identityet vérifiez la réponse. Si vous n’avez pas de session active, consultez Connexion à l’aide de l’AWS CLI pour obtenir des instructions. -
Ouvrez un terminal ou une fenêtre d’invite de commande et accédez au répertoire de votre projet
getstarted. -
Pour créer votre projet, utilisez la commande suivante :
mvn clean package -
Pour exécuter l’application, exécutez la commande suivante :
mvn exec:java -Dexec.mainClass="org.example.App"
Après avoir consulté le fichier, supprimez l’objet, puis le compartiment.
Réussite
Si votre projet Maven a été créé et exécuté sans erreur, félicitations ! Vous avez créé avec succès votre première application Java à l’aide du kit SDK pour Java 2.x.
Nettoyage
Pour nettoyer les ressources que vous avez créées au cours de ce didacticiel, supprimez le dossier du projetgetstarted.
Consultation de la documentation du kit AWS SDK for Java 2.x
Le Guide du développeur du kit AWS SDK for Java 2.x couvre tous les aspects du kit SDK dans tous les Services AWS. Nous vous recommandons de consulter les rubriques suivantes :
-
Migration de la version 1.x vers la version 2.x : inclut une explication détaillée des différences entre les versions 1.x et 2.x. Cette rubrique contient également des instructions sur la façon d’utiliser les deux versions majeures côte à côte.
-
Guide DynamoDB pour le kit Java 2.x SDK : explique comment effectuer des opérations DynamoDB de base : créer une table, manipuler des éléments et extraire des éléments. Ces exemples utilisent l’interface de bas niveau. Java possède plusieurs interfaces, comme expliqué dans la section suivante : Interfaces prises en charge.
Astuce
Après avoir consulté ces rubriques, ajoutez la Référence d’API du kit AWS SDK for Java 2.x
Interfaces prises en charge
Le kit AWS SDK for Java 2.x prend en charge les interfaces suivantes, en fonction du niveau d’abstraction souhaité.
Rubriques de cette section
Interface de bas niveau
L’interface de bas niveau fournit un mappage un-à-un vers l’API de service sous-jacente. Chaque API DynamoDB est disponible via cette interface. Cela signifie que l’interface de bas niveau peut fournir des fonctionnalités complètes, mais elle est souvent plus détaillée et complexe à utiliser. Par exemple, vous devez utiliser les fonctions .s() pour contenir des chaînes et les fonctions .n() pour contenir des nombres. L’exemple suivant de PutItem insère un élément à l’aide de l’interface de bas niveau.
import org.slf4j.*; import software.amazon.awssdk.http.crt.AwsCrtHttpClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.*; import java.util.Map; public class PutItem { // Create a DynamoDB client with the default settings connected to the DynamoDB // endpoint in the default region based on the default credentials provider chain. private static final DynamoDbClient DYNAMODB_CLIENT = DynamoDbClient.create(); private static final Logger LOGGER = LoggerFactory.getLogger(PutItem.class); private void putItem() { PutItemResponse response = DYNAMODB_CLIENT.putItem(PutItemRequest.builder() .item(Map.of( "pk", AttributeValue.builder().s("123").build(), "sk", AttributeValue.builder().s("cart#123").build(), "item_data", AttributeValue.builder().s("YourItemData").build(), "inventory", AttributeValue.builder().n("500").build() // ... more attributes ... )) .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL) .tableName("YourTableName") .build()); LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)"); } }
Interface de haut niveau
L’interface de haut niveau du kit AWS SDK for Java 2.x est appelée client amélioré DynamoDB. Cette interface fournit une expérience de création de code plus idiomatique.
Le client amélioré permet de mapper les classes de données côté client et les tables DynamoDB conçues pour stocker ces données. Vous définissez les relations entre des tables et leurs classes de modèle correspondantes dans votre code. Vous pouvez ensuite compter sur le kit SDK pour gérer la manipulation des types de données. Pour plus d’informations sur le client amélioré, consultez DynamoDB enhanced client API dans le Guide du développeur du kit AWS SDK for Java 2.x.
L’exemple suivant de PutItem utilise l’interface de haut niveau. Dans cet exemple, le DynamoDbBean nommé YourItem crée un TableSchema qui permet son utilisation directe comme entrée pour l’appel de putItem().
import org.slf4j.*; import software.amazon.awssdk.enhanced.dynamodb.*; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*; import software.amazon.awssdk.enhanced.dynamodb.model.*; import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; public class DynamoDbEnhancedClientPutItem { private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build(); private static final DynamoDbTable<YourItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromBean(YourItem.class)); private static final Logger LOGGER = LoggerFactory.getLogger(PutItem.class); private void putItem() { PutItemEnhancedResponse<YourItem> response = DYNAMODB_TABLE.putItemWithResponse(PutItemEnhancedRequest.builder(YourItem.class) .item(new YourItem("123", "cart#123", "YourItemData", 500)) .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL) .build()); LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)"); } @DynamoDbBean public static class YourItem { public YourItem() {} public YourItem(String pk, String sk, String itemData, int inventory) { this.pk = pk; this.sk = sk; this.itemData = itemData; this.inventory = inventory; } private String pk; private String sk; private String itemData; private int inventory; @DynamoDbPartitionKey public void setPk(String pk) { this.pk = pk; } public String getPk() { return pk; } @DynamoDbSortKey public void setSk(String sk) { this.sk = sk; } public String getSk() { return sk; } public void setItemData(String itemData) { this.itemData = itemData; } public String getItemData() { return itemData; } public void setInventory(int inventory) { this.inventory = inventory; } public int getInventory() { return inventory; } } }
Le kit AWS SDK pour Java 1.x possède sa propre interface de haut niveau, souvent désignée par sa classe principale DynamoDBMapper. Le kit AWS SDK for Java 2.x est publié dans un package distinct (et un artefact Maven) nommé software.amazon.awssdk.enhanced.dynamodb. Le kit Java 2.x SDK est souvent désigné par sa classe principale DynamoDbEnhancedClient.
Interface de haut niveau utilisant des classes de données immuables
La fonctionnalité de mappage de l’API client améliorée DynamoDB fonctionne également avec des classes de données immuables. Une classe immuable ne possède que des méthodes getter et nécessite une classe de générateur que le kit SDK utilise pour créer des instances de la classe. L’immuabilité en Java est un style couramment utilisé que les développeurs peuvent utiliser pour créer des classes sans effets secondaires. Le comportement de ces classes est plus prévisible dans les applications multithread complexes. Au lieu d’utiliser l’annotation @DynamoDbBean comme indiqué dans l’High-level interface example, les classes immuables utilisent l’annotation @DynamoDbImmutable, qui prend la classe de générateur comme entrée.
L’exemple suivant utilise la classe de générateur DynamoDbEnhancedClientImmutablePutItem comme entrée pour créer un schéma de table. L’exemple fournit ensuite le schéma en tant qu’entrée pour l’appel d’API PutItem.
import org.slf4j.*; import software.amazon.awssdk.enhanced.dynamodb.*; import software.amazon.awssdk.enhanced.dynamodb.model.*; import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; public class DynamoDbEnhancedClientImmutablePutItem { private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build(); private static final DynamoDbTable<YourImmutableItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromImmutableClass(YourImmutableItem.class)); private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedClientImmutablePutItem.class); private void putItem() { PutItemEnhancedResponse<YourImmutableItem> response = DYNAMODB_TABLE.putItemWithResponse(PutItemEnhancedRequest.builder(YourImmutableItem.class) .item(YourImmutableItem.builder() .pk("123") .sk("cart#123") .itemData("YourItemData") .inventory(500) .build()) .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL) .build()); LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)"); } }
L’exemple suivant illustre la classe de données immuables.
@DynamoDbImmutable(builder = YourImmutableItem.YourImmutableItemBuilder.class) class YourImmutableItem { private final String pk; private final String sk; private final String itemData; private final int inventory; public YourImmutableItem(YourImmutableItemBuilder builder) { this.pk = builder.pk; this.sk = builder.sk; this.itemData = builder.itemData; this.inventory = builder.inventory; } public static YourImmutableItemBuilder builder() { return new YourImmutableItemBuilder(); } @DynamoDbPartitionKey public String getPk() { return pk; } @DynamoDbSortKey public String getSk() { return sk; } public String getItemData() { return itemData; } public int getInventory() { return inventory; } static final class YourImmutableItemBuilder { private String pk; private String sk; private String itemData; private int inventory; private YourImmutableItemBuilder() {} public YourImmutableItemBuilder pk(String pk) { this.pk = pk; return this; } public YourImmutableItemBuilder sk(String sk) { this.sk = sk; return this; } public YourImmutableItemBuilder itemData(String itemData) { this.itemData = itemData; return this; } public YourImmutableItemBuilder inventory(int inventory) { this.inventory = inventory; return this; } public YourImmutableItem build() { return new YourImmutableItem(this); } } }
Interface de haut niveau utilisant des classes de données immuables et des bibliothèques de génération standard tierces
Les classes de données immuables (illustrées dans l’exemple précédent) nécessitent un code standard. Par exemple, la logique getter et setter appliquée aux classes de données, en plus des classes Builder. Les bibliothèques tierces, telles que Project Lombok
L’exemple suivant montre comment Project Lombok simplifie le code nécessaire à l’utilisation de l’API client améliorée DynamoDB.
import org.slf4j.*; import software.amazon.awssdk.enhanced.dynamodb.*; import software.amazon.awssdk.enhanced.dynamodb.model.*; import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; public class DynamoDbEnhancedClientImmutableLombokPutItem { private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build(); private static final DynamoDbTable<YourImmutableLombokItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromImmutableClass(YourImmutableLombokItem.class)); private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedClientImmutableLombokPutItem.class); private void putItem() { PutItemEnhancedResponse<YourImmutableLombokItem> response = DYNAMODB_TABLE.putItemWithResponse(PutItemEnhancedRequest.builder(YourImmutableLombokItem.class) .item(YourImmutableLombokItem.builder() .pk("123") .sk("cart#123") .itemData("YourItemData") .inventory(500) .build()) .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL) .build()); LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)"); } }
L’exemple suivant illustre l’objet de données immuables de la classe de données immuables.
import lombok.*; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*; @Builder @DynamoDbImmutable(builder = YourImmutableLombokItem.YourImmutableLombokItemBuilder.class) @Value public class YourImmutableLombokItem { @Getter(onMethod_=@DynamoDbPartitionKey) String pk; @Getter(onMethod_=@DynamoDbSortKey) String sk; String itemData; int inventory; }
La classe YourImmutableLombokItem utilise les annotations suivantes fournies par Project Lombok et le kit AWS SDK :
-
@Builder
: produit des API de générateur complexes pour les classes de données fournies par Project Lombok. -
@DynamoDbImmutable
: identifie la classe DynamoDbImmutableen tant qu’annotation d’entité mappable DynamoDB fournie par le kit AWS SDK. -
@Value
: variante immuable de @Data. Par défaut, tous les champs sont définis comme privés et définitifs, et les méthodes setter ne sont pas générées. Project Lombok fournit cette annotation.
Interface de document
L’interface de document du kit AWS SDK for Java 2.x évite de devoir spécifier des descripteurs de type de données. Les types de données découlent de la sémantique des données proprement dites. Cette interface de document est similaire à l’interface de document du kit AWS SDK pour Java 1.x, mais avec une interface repensée.
L’Document interface example suivant montre l’appel de PutItem exprimé à l’aide de l’interface de document. L’exemple utilise également EnhancedDocument. Pour exécuter des commandes sur une table DynamoDB à l’aide de l’API de document améliorée, vous devez d’abord associer la table au schéma de votre table de documents pour créer un objet de ressource DynamoDBTable. Le générateur de schéma de table de documents nécessite les principaux fournisseurs de clés d’index et de convertisseurs d’attributs.
Vous pouvez utiliser AttributeConverterProvider.defaultProvider() pour convertir les attributs de document des types par défaut. Vous pouvez modifier le comportement général par défaut à l’aide d’une implémentation de AttributeConverterProvider personnalisée. Vous pouvez également modifier le convertisseur pour un seul attribut. Le Guide de référence des kits AWS SDK et des outils fournit plus de détails et des exemples sur l’utilisation de convertisseurs personnalisés. Ils sont principalement utilisés pour les attributs des classes de domaine pour lesquels aucun convertisseur par défaut n’est disponible. À l’aide d’un convertisseur personnalisé, vous pouvez fournir au kit SDK les informations nécessaires pour écrire ou lire dans DynamoDB.
import org.slf4j.*; import software.amazon.awssdk.enhanced.dynamodb.*; import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; import software.amazon.awssdk.enhanced.dynamodb.model.*; import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; public class DynamoDbEnhancedDocumentClientPutItem { private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build(); private static final DynamoDbTable<EnhancedDocument> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.documentSchemaBuilder() .addIndexPartitionKey(TableMetadata.primaryIndexName(),"pk", AttributeValueType.S) .addIndexSortKey(TableMetadata.primaryIndexName(), "sk", AttributeValueType.S) .attributeConverterProviders(AttributeConverterProvider.defaultProvider()) .build()); private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedDocumentClientPutItem.class); private void putItem() { PutItemEnhancedResponse<EnhancedDocument> response = DYNAMODB_TABLE.putItemWithResponse( PutItemEnhancedRequest.builder(EnhancedDocument.class) .item( EnhancedDocument.builder() .attributeConverterProviders(AttributeConverterProvider.defaultProvider()) .putString("pk", "123") .putString("sk", "cart#123") .putString("item_data", "YourItemData") .putNumber("inventory", 500) .build()) .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL) .build()); LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)"); } }
Pour convertir des documents JSON en types de données Amazon DynamoDB natifs, vous pouvez utiliser les méthodes utilitaires suivantes :
-
EnhancedDocument.fromJson(String json): crée une nouvelle instance EnhancedDocument à partir d’une chaîne JSON. -
EnhancedDocument.toJson(): crée une représentation sous forme de chaîne JSON du document que vous pouvez utiliser dans votre application comme n’importe quel autre objet JSON.
Comparaison d’interfaces avec un exemple de Query
Cette section montre le même appel de Query exprimé à l’aide des différentes interfaces. Pour optimiser les résultats de ces requêtes, notez ce qui suit :
-
DynamoDB cible une valeur de clé de partition spécifique. Vous devez donc spécifier la clé de partition dans son intégralité.
-
Pour que la requête cible uniquement les articles du panier, la clé de tri possède une expression de condition clé qui utilise
begins_with. -
Nous utilisons
limit()pour limiter la requête à un maximum de 100 articles renvoyés. -
Nous avons défini
scanIndexForwardsur false. Les résultats sont renvoyés dans l’ordre des octets UTF-8, ce qui signifie généralement que l’article du panier avec le numéro le plus bas est renvoyé en premier. En définissantscanIndexForwardsur false, nous annulons la commande et l’article du panier avec le numéro le plus élevé est renvoyé en premier. -
Nous appliquons un filtre pour supprimer tout résultat ne correspondant pas aux critères. Les données filtrées consomment de la capacité de lecture, que l’élément corresponde au filtre ou non.
Exemple Query avec l’interface de bas niveau
L’exemple suivant interroge un élément à partir de la table nommée YourTableName à l’aide d’une keyConditionExpression. Cela limite la requête à une valeur de clé de partition spécifique et à une valeur de clé de tri commençant par une valeur de préfixe spécifique. Ces conditions clés limitent la quantité de données lues depuis DynamoDB. Enfin, la requête applique un filtre sur les données extraites de DynamoDB à l’aide d’une filterExpression.
import org.slf4j.*; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.*; import java.util.Map; public class Query { // Create a DynamoDB client with the default settings connected to the DynamoDB // endpoint in the default region based on the default credentials provider chain. private static final DynamoDbClient DYNAMODB_CLIENT = DynamoDbClient.builder().build(); private static final Logger LOGGER = LoggerFactory.getLogger(Query.class); private static void query() { QueryResponse response = DYNAMODB_CLIENT.query(QueryRequest.builder() .expressionAttributeNames(Map.of("#name", "name")) .expressionAttributeValues(Map.of( ":pk_val", AttributeValue.fromS("id#1"), ":sk_val", AttributeValue.fromS("cart#"), ":name_val", AttributeValue.fromS("SomeName"))) .filterExpression("#name = :name_val") .keyConditionExpression("pk = :pk_val AND begins_with(sk, :sk_val)") .limit(100) .scanIndexForward(false) .tableName("YourTableName") .build()); LOGGER.info("nr of items: " + response.count()); LOGGER.info("First item pk: " + response.items().get(0).get("pk")); LOGGER.info("First item sk: " + response.items().get(0).get("sk")); } }
Exemple Query avec l’interface de document
L’exemple suivant interroge une table nommée YourTableName à l’aide de l’interface de document.
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.enhanced.dynamodb.*; import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; import software.amazon.awssdk.enhanced.dynamodb.model.*; import java.util.Map; public class DynamoDbEnhancedDocumentClientQuery { // Create a DynamoDB client with the default settings connected to the DynamoDB // endpoint in the default region based on the default credentials provider chain. private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build(); private static final DynamoDbTable<EnhancedDocument> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.documentSchemaBuilder() .addIndexPartitionKey(TableMetadata.primaryIndexName(),"pk", AttributeValueType.S) .addIndexSortKey(TableMetadata.primaryIndexName(), "sk", AttributeValueType.S) .attributeConverterProviders(AttributeConverterProvider.defaultProvider()) .build()); private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedDocumentClientQuery.class); private void query() { PageIterable<EnhancedDocument> response = DYNAMODB_TABLE.query(QueryEnhancedRequest.builder() .filterExpression(Expression.builder() .expression("#name = :name_val") .expressionNames(Map.of("#name", "name")) .expressionValues(Map.of(":name_val", AttributeValue.fromS("SomeName"))) .build()) .limit(100) .queryConditional(QueryConditional.sortBeginsWith(Key.builder() .partitionValue("id#1") .sortValue("cart#") .build())) .scanIndexForward(false) .build()); LOGGER.info("nr of items: " + response.items().stream().count()); LOGGER.info("First item pk: " + response.items().iterator().next().getString("pk")); LOGGER.info("First item sk: " + response.items().iterator().next().getString("sk")); } }
Exemple Query avec l’interface de haut niveau
L’exemple suivant interroge une table nommée YourTableName à l’aide de l’API client améliorée DynamoDB.
import org.slf4j.*; import software.amazon.awssdk.enhanced.dynamodb.*; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*; import software.amazon.awssdk.enhanced.dynamodb.model.*; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import java.util.Map; public class DynamoDbEnhancedClientQuery { private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build(); private static final DynamoDbTable<YourItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromBean(DynamoDbEnhancedClientQuery.YourItem.class)); private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedClientQuery.class); private void query() { PageIterable<YourItem> response = DYNAMODB_TABLE.query(QueryEnhancedRequest.builder() .filterExpression(Expression.builder() .expression("#name = :name_val") .expressionNames(Map.of("#name", "name")) .expressionValues(Map.of(":name_val", AttributeValue.fromS("SomeName"))) .build()) .limit(100) .queryConditional(QueryConditional.sortBeginsWith(Key.builder() .partitionValue("id#1") .sortValue("cart#") .build())) .scanIndexForward(false) .build()); LOGGER.info("nr of items: " + response.items().stream().count()); LOGGER.info("First item pk: " + response.items().iterator().next().getPk()); LOGGER.info("First item sk: " + response.items().iterator().next().getSk()); } @DynamoDbBean public static class YourItem { public YourItem() {} public YourItem(String pk, String sk, String name) { this.pk = pk; this.sk = sk; this.name = name; } private String pk; private String sk; private String name; @DynamoDbPartitionKey public void setPk(String pk) { this.pk = pk; } public String getPk() { return pk; } @DynamoDbSortKey public void setSk(String sk) { this.sk = sk; } public String getSk() { return sk; } public void setName(String name) { this.name = name; } public String getName() { return name; } } }
Interface de haut niveau utilisant des classes de données immuables
Lorsque vous effectuez une opération Query avec les classes de données immuables de haut niveau, le code est le même que celui de l’exemple d’interface de haut niveau, à l’exception de la construction de la classe d’entité YourItem ou YourImmutableItem. Pour plus d’informations, consultez l’exemple PutItem.
Interface de haut niveau utilisant des classes de données immuables et des bibliothèques de génération standard tierces
Lorsque vous effectuez une opération Query avec les classes de données immuables de haut niveau, le code est le même que celui de l’exemple d’interface de haut niveau, à l’exception de la construction de la classe d’entité YourItem ou YourImmutableLombokItem. Pour plus d’informations, consultez l’exemple PutItem.
Exemples supplémentaires de code
Pour des exemples supplémentaires d’utilisation de DynamoDB avec le kit SDK pour Java 2.x, reportez-vous aux référentiels d’exemples de code suivants :
Programmation synchrone et asynchrone
Le kit AWS SDK for Java 2.x fournit des clients synchrones et asynchrones pour les Services AWS, tels que DynamoDB.
Les classes DynamoDbClient et DynamoDbEnhancedClient fournissent des méthodes synchrones qui bloquent l’exécution du thread jusqu’à ce que le client reçoive une réponse du service. Ce client est le moyen le plus simple d’interagir avec DynamoDB si vous n’avez pas besoin d’opérations asynchrones.
Les classes DynamoDbAsyncClient et DynamoDbEnhancedAsyncClient fournissent des méthodes asynchrones qui renvoient immédiatement, en rendant le contrôle au thread appelant sans attendre de réponse. Le client non bloquant présente l’avantage d’une simultanéité élevée sur quelques threads, ce qui permet de traiter efficacement les demandes d’E/S avec un minimum de ressources de calcul. Cela améliore le débit et la réactivité.
Le kit AWS SDK for Java 2.x utilise le support natif pour les E/S non bloquantes. La version du kit AWS SDK pour Java 1.x devait simuler des E/S non bloquantes.
Dans la mesure où une méthode synchrone renvoie avant qu’une réponse ne soit disponible, vous avez besoin d’une solution pour obtenir la réponse quand elle est prête. Les méthodes asynchrones du kit AWS SDK pour Java renvoient un objet CompletableFutureget() ou utilisez join() sur ces objets CompletableFuture, le code se bloque jusqu’à ce que le résultat soit disponible. Si vous les appelez en même temps que vous faites la demande, le comportement est similaire à celui d’un simple appel synchrone.
Pour plus d’informations sur la programmation asynchrone, consultez Use asynchronous programming dans le Guide du développeur du kit AWS SDK for Java 2.x.
Clients HTTP
Pour prendre en charge chaque client, il existe un client HTTP qui gère les communications avec les Services AWS. Vous pouvez connecter d’autres clients HTTP en choisissant celui qui présente les caractéristiques les mieux adaptées à votre application. Certains sont plus légers ; d’autres offrent davantage d’options de configuration.
Certains clients HTTP ne prennent en charge que l’utilisation synchrone, tandis que d’autres prennent uniquement en charge l’utilisation asynchrone. Pour un organigramme qui peut vous aider à sélectionner le client HTTP optimal pour votre charge de travail, consultez HTTP client recommendations dans le Guide du développeur du kit AWS SDK for Java 2.x.
La liste suivante présente certains des clients HTTP possibles :
Rubriques
Client HTTP basé sur Apache
La classe ApacheHttpClientApacheHttpClient, consultez Configuration du client HTTP basé sur Apache dans le Guide du développeur du kit AWS SDK for Java 2.x.
Client HTTP basé sur URLConnection
La classe UrlConnectionHttpClientUrlConnectionHttpClient, consultez Configure the URLConnection-based HTTP client dans le Guide du développeur du kit AWS SDK for Java 2.x.
Client HTTP basé sur Netty
La classe NettyNioAsyncHttpClient prend en charge les clients asynchrones. C’est le choix par défaut pour une utilisation asynchrone. Pour en savoir plus sur la configuration de la classe NettyNioAsyncHttpClient, consultez Configure the Netty-based HTTP client dans le Guide du développeur du kit AWS SDK for Java 2.x.
Client HTTP basé sur AWS CRT
Les nouvelles classes AwsCrtHttpClient et AwsCrtAsyncHttpClient des bibliothèques AWS Common Runtime (CRT) sont davantage des options qui prennent en charge les clients synchrones et asynchrones. Comparé aux autres clients HTTP, AWS CRT propose :
-
Temps de démarrage plus rapide du kit SDK
-
Empreinte mémoire réduite
-
Temps de latence réduit
-
Gestion de l’état des connexions
-
Équilibrage de charge DNS
Pour en savoir plus sur la configuration des classes AwsCrtHttpClient et AwsCrtAsyncHttpClient, consultez Configure the AWS CRT-based HTTP clients dans le Guide du développeur du kit AWS SDK for Java 2.x.
Le client HTTP basé sur AWS CRT n’est pas le client par défaut, car cela romprait la rétrocompatibilité des applications existantes. Toutefois, pour DynamoDB, nous vous recommandons d’utiliser le client HTTP basé sur AWS CRT pour les utilisations synchrones et asynchrones.
Pour une présentation du client HTTP basé sur AWS CRT, consultez Announcing availability of the AWS CRT HTTP Client in the AWS SDK for Java 2.x
Configuration d’un client HTTP
Lorsque vous configurez un client, vous pouvez proposer différentes options de configuration, notamment :
-
Définition de délais d’expiration pour différents aspects des appels d’API.
-
Activation de TCP Keep-Alive.
-
Contrôle de la politique de nouvelles tentatives en cas d’erreur.
-
Spécification des attributs d’exécution que les instances d’intercepteur d’exécution peuvent modifier. Les intercepteurs d’exécution peuvent écrire du code qui intercepte l’exécution des demandes et réponses d’API. Cela vous permet d’effectuer des tâches telles que la publication de métriques et la modification des demandes en cours de route.
-
Ajout ou manipulation des en-têtes HTTP.
-
Activation du suivi des métriques de performance côté client. L’utilisation de cette fonctionnalité vous permet de collecter des métriques sur les clients de service dans votre application, et d’analyser la sortie dans Amazon CloudWatch.
-
Spécification d’un autre service d’exécuteur à utiliser pour planifier les tâches, telles que les nouvelles tentatives asynchrones et les tâches de délai d’expiration.
Vous contrôlez la configuration en fournissant un objet ClientOverrideConfigurationBuilder du client de service. Vous le verrez dans certains exemples de code dans les sections suivantes.
La ClientOverrideConfiguration fournit des choix de configuration standard. Les différents clients HTTP enfichables offrent également des possibilités de configuration spécifiques à l’implémentation.
Rubriques de cette section
Configuration du délai d’attente
Vous pouvez ajuster la configuration du client pour contrôler les différents délais d’expiration liés aux appels de service. DynamoDB fournit des latences plus faibles que les autres Services AWS. Par conséquent, vous souhaiterez peut-être ajuster ces propriétés pour réduire les valeurs de délai d’expiration afin de pouvoir procéder à une interruption immédiate en cas de problème réseau.
Vous pouvez personnaliser le comportement lié à la latence à l’aide de la ClientOverrideConfiguration du client DynamoDB ou en modifiant les options de configuration détaillées sur l’implémentation du client HTTP sous-jacent.
Vous pouvez configurer les propriétés percutantes suivantes à l’aide de la ClientOverrideConfiguration :
-
apiCallAttemptTimeout: temps d’attente d’une seule tentative pour qu’une demande HTTP soit terminée avant d’abandonner et de dépasser le délai imparti. -
apiCallTimeout: durée dont dispose le client pour exécuter complètement un appel d’API. Cela inclut l’exécution du gestionnaire de demandes qui comprend toutes les demandes HTTP, y compris les nouvelles tentatives.
Le kit AWS SDK for Java 2.x fournit des valeurs par défautClientOverrideConfiguration, le kit SDK utilise plutôt la valeur du délai d’expiration du socket pour le délai d’expiration global d’appel d’API. La délai d’expiration du socket a une valeur par défaut de 30 secondes.
RetryMode
Une autre configuration liée à la configuration du délai d’expiration que vous devez prendre en compte est l’objet de configuration RetryMode. Cet objet de configuration contient un ensemble de comportements de nouvelle tentative.
Le kit SDK pour Java 2.x est compatible avec les modes de nouvelle tentative suivants :
-
legacy: mode de nouvelle tentative par défaut si vous ne le modifiez pas explicitement. Ce mode de nouvelle tentative est spécifique au kit Java SDK. Il se caractérise par un maximum de trois nouvelles tentatives, voire plus pour des services tels que DynamoDB, qui compte jusqu’à huit nouvelles tentatives. -
standard: nommé « standard », car il est plus cohérent avec les autres kits AWS SDK. Ce mode attend une durée aléatoire comprise entre 0 ms et 1 000 ms pour la première nouvelle tentative. Si une autre nouvelle tentative est nécessaire, ce mode choisit un autre temps aléatoire compris entre 0 ms et 1 000 ms et le multiplie par deux. Si une nouvelle tentative supplémentaire est nécessaire, il effectue le même choix aléatoire multiplié par quatre, et ainsi de suite. Chaque attente est limitée à 20 secondes. Ce mode effectue de nouvelles tentatives sur un plus grand nombre de conditions de défaillance détectées que le modelegacylui-même. Pour DynamoDB, il effectue jusqu’à trois tentatives maximum au total, sauf si vous remplacez la valeur par numRetries. -
adaptive: s’appuie sur le modestandardet limite dynamiquement le taux de demandes AWS afin de maximiser le taux de réussite. Cela peut se produire au détriment de la latence de demande. Nous ne recommandons pas le mode de nouvelle tentative adaptatif lorsqu’une latence prévisible est importante.
Vous trouverez une définition détaillée de ces modes de nouvelle tentative dans la rubrique Comportement des nouvelles tentatives du Guide de référence des kits AWS SDK et des outils.
Politiques de nouvelles tentatives
Toutes les configurations de RetryMode ont une RetryPolicyRetryConditionTokenBucketRetryConditionTokenBucket.
Lorsqu’un client rencontre une erreur pouvant faire l’objet d’une nouvelle tentative, telle qu’une exception de limitation ou une erreur temporaire du serveur, le kit SDK fait automatiquement une nouvelle tentative de demande. Vous pouvez contrôler le nombre de nouvelles tentatives et la rapidité avec lesquelles elles ont lieu.
Lorsque vous configurez un client, vous pouvez fournir une RetryPolicy qui prend en charge les paramètres suivants :
-
numRetries: nombre maximum de nouvelles tentatives qui doivent être appliquées avant qu’une demande ne soit considérée comme ayant échoué. La valeur par défaut est 8 quel que soit le mode de nouvelle tentative que vous utilisez.Avertissement
Assurez-vous de modifier cette valeur par défaut après mûre réflexion.
-
backoffStrategy: laBackoffStrategyà appliquer aux nouvelles tentatives, FullJitterBackoffStrategyétant la stratégie par défaut. Cette stratégie applique un délai exponentiel entre les nouvelles tentatives supplémentaires en fonction du nombre actuel de nouvelles tentatives, d’un délai de base et d’un temps de backoff maximal. Cela ajoute ensuite de la nervosité pour créer un peu de hasard. Le délai de base utilisé dans le délai exponentiel est de 25 ms quel que soit le mode de nouvelle tentative. -
retryCondition: laRetryConditiondétermine si une demande doit faire l’objet d’une nouvelle tentative. Par défaut, elle effectue de nouvelles tentatives pour un ensemble spécifique de codes de statut HTTP et d’exceptions pour lesquels juge qu’il est possible d’effectuer de nouvelles tentatives. Dans la plupart des cas, la configuration par défaut devrait être suffisante.
Le code suivant fournit une politique de nouvelles tentatives alternative. Il indique un total de cinq nouvelles tentatives (six demandes au total). La première nouvelle tentative doit avoir lieu après un délai d’environ 100 ms, chaque nouvelle tentative doublant ce temps de façon exponentielle, jusqu’à un délai maximum d’une seconde.
DynamoDbClient client = DynamoDbClient.builder() .overrideConfiguration(ClientOverrideConfiguration.builder() .retryPolicy(RetryPolicy.builder() .backoffStrategy(FullJitterBackoffStrategy.builder() .baseDelay(Duration.ofMillis(100)) .maxBackoffTime(Duration.ofSeconds(1)) .build()) .numRetries(5) .build()) .build()) .build();
DefaultsMode
Les propriétés de délai d’expiration que la ClientOverrideConfiguration et le RetryMode ne gèrent pas sont généralement configurées implicitement en spécifiant un DefaultsMode.
Le kit AWS SDK for Java 2.x (version 2.17.102 ou ultérieure) a introduit la prise en charge du DefaultsMode. Cette fonctionnalité fournit un ensemble de valeurs par défaut pour les paramètres configurables courants, tels que les paramètres de communication HTTP, le comportement des nouvelles tentatives, les paramètres de point de terminaison régionaux du service et potentiellement toute configuration liée au kit SDK. Lorsque vous utilisez cette fonctionnalité, vous pouvez obtenir de nouvelles valeurs de configuration par défaut adaptées aux scénarios d’utilisation courants.
Les modes par défaut sont standardisés dans tous les kits AWS SDK. Le kit SDK pour Java 2.x est compatible avec les modes par défaut suivants :
-
legacy: fournit des paramètres par défaut qui varient selon le kit AWS SDK et qui existaient avant la création duDefaultsMode. -
standard: fournit des paramètres non optimisés par défaut pour la plupart des scénarios. -
in-region: s’appuie sur le mode standard et inclut des paramètres adaptés aux applications qui appellent les Services AWS depuis la même Région AWS. -
cross-region: s’appuie sur le mode standard et inclut des paramètres avec des délais d’expiration élevés pour les applications qui appellent les Services AWS dans une région différente. -
mobile: s’appuie sur le mode standard et inclut des paramètres avec des délais d’expiration élevés adaptés aux applications mobiles présentant des latences plus élevées. -
auto: s’appuie sur le mode standard et inclut des fonctionnalités expérimentales. Le kit SDK tente de découvrir l’environnement d’exécution afin de déterminer automatiquement les paramètres appropriés. La détection automatique est basée sur l’heuristique et ne fournit pas une précision de 100 %. Si l’environnement d’exécution ne peut pas être déterminé, le mode standard est utilisé. La détection automatique peut interroger les métadonnées de l’instance et les données utilisateur, ce qui peut introduire de la latence. Si la latence de démarrage est essentielle pour votre application, nous vous recommandons de choisir plutôt unDefaultsModeexplicite.
Vous pouvez configurer le mode de valeurs par défaut des manières suivantes :
-
Directement sur un client, par le biais de
AwsClientBuilder.Builder#defaultsMode(DefaultsMode). -
Sur un profil de configuration, par le biais de la propriété du fichier de profil
defaults_mode. -
Globalement, par le biais de la propriété système
aws.defaultsMode. -
Globalement, par le biais de la variable d’environnement
AWS_DEFAULTS_MODE.
Note
Pour tout mode autre que legacy, les valeurs par défaut fournies peuvent changer à mesure que les bonnes pratiques évoluent. Par conséquent, si vous utilisez un mode autre que legacy, nous vous encourageons à effectuer des tests lors de la mise à niveau du kit SDK.
Paramètres de configuration intelligente dans le Guide de référence des kits AWS SDK et des outils fournit une liste des propriétés de configuration et de leurs valeurs par défaut dans les différents modes par défaut.
Vous choisissez la valeur du mode par défaut en fonction des caractéristiques de votre application et du Service AWS avec lequel l’application interagit.
Ces valeurs sont configurées en tenant compte d’une large sélection de Services AWS. Pour un déploiement de DynamoDB classique dans lequel les tables DynamoDB et l’application sont déployées dans une seule région, le mode par défaut in-region est le plus pertinent parmi les modes par défaut standard.
Exemple Configuration du client du kit SDK DynamoDB optimisée pour les appels à faible latence
L’exemple suivant ajuste les délais d’expiration afin de réduire les valeurs d’un appel DynamoDB à faible latence attendu.
DynamoDbAsyncClient asyncClient = DynamoDbAsyncClient.builder() .defaultsMode(DefaultsMode.IN_REGION) .httpClientBuilder(AwsCrtAsyncHttpClient.builder()) .overrideConfiguration(ClientOverrideConfiguration.builder() .apiCallTimeout(Duration.ofSeconds(3)) .apiCallAttemptTimeout(Duration.ofMillis(500)) .build()) .build();
L’implémentation individuelle du client HTTP peut vous permettre de contrôler encore plus précisément le délai d’expiration et le comportement d’utilisation de la connexion. Par exemple, pour le client AWS basé sur CRT, vous pouvez activer ConnectionHealthConfiguration, ce qui permet au client de surveiller activement l’état des connexions utilisées. Pour plus d’informations, consultez Advanced configuration of AWS CRT-based HTTP clients dans le Guide du développeur AWS SDK for Java 2.x.
Configuration de Keep-Alive
L’activation du mode keep-alive permet de réduire les latences en réutilisant les connexions. Il existe deux types de keep-alive : HTTP Keep-Alive et TCP Keep-Alive.
-
HTTP Keep-Alive tente de maintenir la connexion HTTPS entre le client et le serveur afin que les demandes ultérieures puissent réutiliser cette connexion. Cela permet d’éviter l’authentification HTTPS lourde lors des demandes ultérieures. HTTP Keep-Alive est activé par défaut sur tous les clients.
-
TCP Keep-Alive demande au système d’exploitation sous-jacent d’envoyer de petits paquets via la connexion socket afin de garantir que le socket est maintenu en vie et de détecter immédiatement tout abandon. Cela garantit qu’une demande ultérieure ne perdra pas de temps à essayer d’utiliser un socket abandonné. Par défaut, TCP Keep-Alive est désactivé sur tous les clients. Les exemples de code suivants montrent comment l’activer sur chaque client HTTP. Lorsqu’il est activé pour tous les clients HTTP non basés sur CRT, le mécanisme Keep-Alive réel dépend du système d’exploitation. Par conséquent, vous devez configurer des valeurs TCP Keep-Alive supplémentaires, telles que le délai d’expiration et le nombre de paquets, via le système d’exploitation. Vous pouvez le faire avec
sysctlsous Linux ou macOS, ou en utilisant les valeurs de registre sous Windows.
Exemple pour activer TCP Keep-Alive sur un client HTTP basé sur Apache
DynamoDbClient client = DynamoDbClient.builder() .httpClientBuilder(ApacheHttpClient.builder().tcpKeepAlive(true)) .build();
Client HTTP basé sur URLConnection
Tout client synchrone qui utilise le client HTTP basé sur URLConnection, HttpURLConnection
Exemple pour activer TCP Keep-Alive sur un client HTTP basé sur Netty
DynamoDbAsyncClient client = DynamoDbAsyncClient.builder() .httpClientBuilder(NettyNioAsyncHttpClient.builder().tcpKeepAlive(true)) .build();
Exemple pour activer TCP Keep-Alive sur un client HTTP basé sur AWS CRT
Avec le client HTTP basé sur AWS CRT, vous pouvez activer TCP Keep-Alive et contrôler la durée.
DynamoDbClient client = DynamoDbClient.builder() .httpClientBuilder(AwsCrtHttpClient.builder() .tcpKeepAliveConfiguration(TcpKeepAliveConfiguration.builder() .keepAliveInterval(Duration.ofSeconds(50)) .keepAliveTimeout(Duration.ofSeconds(5)) .build())) .build();
Lorsque vous utilisez le client DynamoDB asynchrone, vous pouvez activer TCP Keep-Alive comme indiqué dans le code suivant.
DynamoDbAsyncClient client = DynamoDbAsyncClient.builder() .httpClientBuilder(AwsCrtAsyncHttpClient.builder() .tcpKeepAliveConfiguration(TcpKeepAliveConfiguration.builder() .keepAliveInterval(Duration.ofSeconds(50)) .keepAliveTimeout(Duration.ofSeconds(5)) .build())) .build();
Gestion des erreurs
En ce qui concerne la gestion des exceptions, le kit AWS SDK for Java 2.x utilise des exceptions d’exécution (non vérifiées).
L’exception de base, qui couvre toutes les exceptions du kit SDK, est SdkServiceExceptionRuntimeException Java non coché. Si vous détectez cela, vous détecterez toutes les exceptions générées par le kit SDK.
SdkServiceException possède une sous-classe appelée AwsServiceExceptionDynamoDbException
Vous trouverez des types d’exceptionDynamoDbException. Certains de ces types d’exception s’appliquent aux opérations du plan de contrôle, telles que TableAlreadyExistsException
-
ConditionalCheckFailedException: vous avez spécifié une condition dans la demande, qui a été analysée comme false. Par exemple, il se peut que vous avez essayé d’effectuer une mise à jour conditionnelle sur un élément, mais que la valeur réelle de l’attribut ne corresponde pas à la valeur attendue de la condition. Une demande qui échoue de cette manière ne fait pas l’objet d’une nouvelle tentative.
Dans d’autres situations, aucune exception spécifique n’est définie. Par exemple, lorsque vos demandes font l’objet d’une limitation, les ProvisionedThroughputExceededException spécifiques peuvent être générées, tandis que dans d’autres cas, les DynamoDbException plus génériques sont générées. Dans les deux cas, vous pouvez déterminer si la limitation est à l’origine de l’exception en vérifiant si isThrottlingException() renvoie true.
En fonction des besoins de votre application, vous pouvez détecter toute AwsServiceException ou instance DynamoDbException. Cependant, vous avez souvent besoin d’un comportement différent selon les situations. La logique utilisée pour faire face à un échec de vérification d’état est différente de celle utilisée pour gérer la limitation. Définissez les chemins exceptionnels que vous souhaitez traiter et assurez-vous de tester les chemins alternatifs. Cela vous permet de vous assurer que vous pouvez faire face à tous les scénarios pertinents.
Pour obtenir la liste des erreurs courantes que vous pouvez rencontrer, consultez Gestion des erreurs avec DynamoDB. Consultez également Erreurs courantes dans la Référence de l’API Amazon DynamoDB. La référence d’API fournit également les erreurs exactes possibles pour chaque opération d’API, par exemple pour l’opération Query. Pour en savoir plus sur la gestion des exceptions, consultez Exception handling for the AWS SDK for Java 2.x dans le Guide du développeur du kit AWS SDK for Java 2.x.
ID de la demande AWS
Chaque demande inclut un ID de demande, qu’il peut être utile d’extraire si vous utilisez AWS Support pour diagnostiquer un problème. Chaque exception dérivée de SdkServiceException dispose d’une méthode requestId()
Journalisation
L’utilisation de la journalisation fournie par le kit SDK peut être utile à la fois pour détecter les messages importants provenant des bibliothèques clientes et à des fins de débogage plus approfondies. Les enregistreurs sont hiérarchiques et le kit SDK utilise software.amazon.awssdk comme enregistreur racine. Vous pouvez configurer le niveau avec TRACE, DEBUG, INFO, WARN, ERROR, ALL ou OFF. Le niveau configuré s’applique à cet enregistreur et à la hiérarchie des enregistreurs.
Pour sa journalisation, le kit AWS SDK for Java 2.x utilise la façade de journalisation simple pour Java (SLF4J). Cela agit comme une couche d’abstraction autour des autres enregistreurs, et vous pouvez l’utiliser pour connecter l’enregistreur que vous préférez. Pour obtenir des instructions sur le branchement des enregistreurs, consultez le SLF4J user manual
Chaque enregistreur a un comportement particulier. Par défaut, l’enregistreur Log4j 2.x crée un ConsoleAppender, qui ajoute les événements du journal à System.out et les valeurs par défaut au niveau du journal ERROR.
L’enregistreur SimpleLogger inclus dans SLF4J génère des sorties par défaut dans System.err et est défini par défaut sur le niveau de journalisation sur INFO.
Nous vous recommandons de définir le niveau sur WARN pour software.amazon.awssdk pour tous les déploiements de production afin de récupérer les messages importants provenant des bibliothèques clientes du kit SDK tout en limitant la quantité de sortie.
Si SLF4J ne trouve aucun enregistreur compatible sur le chemin de classe (aucune liaison SLF4J), il utilise par défaut une implémentation sans opérationSystem.err expliquant que SLF4J n’a pas trouvé d’implémentation d’enregistreur sur le chemin de classe. Pour éviter cette situation, vous devez ajouter une implémentation d’enregistreur. Pour ce faire, vous pouvez ajouter une dépendance dans votre pom.xml Apache Maven sur des artefacts, tels que org.slf4j.slf4j-simple ou org.apache.logging.log4j.log4j-slf4j2-imp.
Pour plus d’informations sur la configuration de la journalisation dans le kit SDK, notamment sur l’ajout de dépendances de journalisation à la configuration de votre application, consultez Logging with the SDK for Java 2.x dans le Guide du développeur du kit AWS SDK pour Java.
La configuration suivante du fichier Log4j2.xml montre comment ajuster le comportement de journalisation si vous utilisez l’enregistreur Apache Log4j 2. Cette configuration définit le niveau de l’enregistreur racine sur WARN. Tous les enregistreurs de la hiérarchie héritent de ce niveau de journalisation, y compris l’enregistreur software.amazon.awssdk.
Par défaut, la sortie passe à System.out. Dans l’exemple suivant, nous remplaçons toujours l’appender Log4j de sortie par défaut pour appliquer un PatternLayout Log4j adapté.
Exemple de fichier de configuration Log4j2.xml
La configuration suivante enregistre les messages sur la console aux niveaux ERROR et WARN pour toutes les hiérarchies d’enregistreurs.
<Configuration status="WARN"> <Appenders> <Console name="ConsoleAppender" target="SYSTEM_OUT"> <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c:%L - %m%n" /> </Console> </Appenders> <Loggers> <Root level="WARN"> <AppenderRef ref="ConsoleAppender"/> </Root> </Loggers> </Configuration>
Journalisation de l’ID de demande AWS
En cas de problème, vous pouvez trouver des ID de demande dans des exceptions. Toutefois, si vous souhaitez obtenir les ID des demandes qui ne génèrent pas d’exceptions, vous pouvez utiliser la journalisation.
L’enregistreur software.amazon.awssdk.request produit les ID de demande au niveau DEBUG. L’exemple suivant étend l’configuration example précédent pour maintenir le niveau d’enregistreur racine sur ERROR, sur software.amazon.awssdk au niveau WARN, et sur software.amazon.awssdk.request au niveau DEBUG. La définition de ces niveaux permet de récupérer les ID des demandes et d’autres détails liés aux demandes, tels que le point de terminaison et le code de statut.
<Configuration status="WARN"> <Appenders> <Console name="ConsoleAppender" target="SYSTEM_OUT"> <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c:%L - %m%n" /> </Console> </Appenders> <Loggers> <Root level="ERROR"> <AppenderRef ref="ConsoleAppender"/> </Root> <Logger name="software.amazon.awssdk" level="WARN" /> <Logger name="software.amazon.awssdk.request" level="DEBUG" /> </Loggers> </Configuration>
Voici un exemple de la sortie du journal:
2022-09-23 16:02:08 [main] DEBUG software.amazon.awssdk.request:85 - Sending Request: DefaultSdkHttpFullRequest(httpMethod=POST, protocol=https, host=dynamodb.us-east-1.amazonaws.com, encodedPath=/, headers=[amz-sdk-invocation-id, Content-Length, Content-Type, User-Agent, X-Amz-Target], queryParameters=[]) 2022-09-23 16:02:08 [main] DEBUG software.amazon.awssdk.request:85 - Received successful response: 200, Request ID: QS9DUMME2NHEDH8TGT9N5V53OJVV4KQNSO5AEMVJF66Q9ASUAAJG, Extended Request ID: not available
Pagination
Certaines demandes, telles que Query et Scan, limitent la taille des données renvoyées lors d’une seule demande et nécessitent que vous fassiez des demandes répétées pour extraire les pages suivantes.
Vous pouvez contrôler le nombre maximum d’éléments à lire pour chaque page à l’aide du paramètre Limit. Par exemple, vous pouvez utiliser le paramètre Limit pour récupérer uniquement les 10 derniers éléments. Cette limite indique le nombre d’éléments à lire dans la table avant qu’un filtrage ne soit appliqué. Si vous voulez exactement 10 éléments après le filtrage, il n’y a aucun moyen de le spécifier. Vous ne pouvez contrôler que le nombre préfiltré et vérifier côté client lorsque vous avez effectivement extrait 10 éléments. Quelle que soit la limite, les réponse ont toujours une taille maximale de 1 Mo.
Une LastEvaluatedKey peut être incluse dans la réponse de l’API. Cela indique que la réponse s’est terminée parce qu’elle a atteint une limite de nombre ou de taille. La clé est la dernière clé évaluée pour la réponse. En interagissant directement avec l’API, vous pouvez le extraire cette LastEvaluatedKey et la transmettre à un appel de suivi en tant que ExclusiveStartKey afin de lire le fragment suivant à partir de ce point de départ. Si aucune LastEvaluatedKey n’est renvoyée, cela signifie qu’il n’y a plus d’éléments correspondant à l’appel d’API Query ou Scan.
L’exemple suivant utilise l’interface de bas niveau pour limiter les éléments à 100 en fonction du paramètre keyConditionExpression.
QueryRequest.Builder queryRequestBuilder = QueryRequest.builder() .expressionAttributeValues(Map.of( ":pk_val", AttributeValue.fromS("123"), ":sk_val", AttributeValue.fromN("1000"))) .keyConditionExpression("pk = :pk_val AND sk > :sk_val") .limit(100) .tableName(TABLE_NAME); while (true) { QueryResponse queryResponse = DYNAMODB_CLIENT.query(queryRequestBuilder.build()); queryResponse.items().forEach(item -> { LOGGER.info("item PK: [" + item.get("pk") + "] and SK: [" + item.get("sk") + "]"); }); if (!queryResponse.hasLastEvaluatedKey()) { break; } queryRequestBuilder.exclusiveStartKey(queryResponse.lastEvaluatedKey()); }
Le kit AWS SDK for Java 2.x peut simplifier cette interaction avec DynamoDB en fournissant des méthodes de pagination automatique qui effectuent plusieurs appels de service pour obtenir automatiquement les pages de résultats suivantes pour vous. Cela simplifie le code, mais vous prive d’un certain contrôle de l’utilisation des ressources que vous pourriez conserver en lisant les pages manuellement.
En utilisant les méthodes Iterable disponibles dans le client DynamoDB, telles que QueryPaginatorScanPaginatorQueryPaginator comme illustré dans l’exemple suivant.
QueryPublisher queryPublisher = DYNAMODB_CLIENT.queryPaginator(QueryRequest.builder() .expressionAttributeValues(Map.of( ":pk_val", AttributeValue.fromS("123"), ":sk_val", AttributeValue.fromN("1000"))) .keyConditionExpression("pk = :pk_val AND sk > :sk_val") .limit(100) .tableName("YourTableName") .build()); queryPublisher.items().subscribe(item -> System.out.println(item.get("itemData"))).join();
Annotations de classes de données
Le kit SDK Java fournit plusieurs annotations que vous pouvez ajouter aux attributs de la classe de données. Ces annotations influencent la manière dont le kit SDK interagit avec les attributs. En ajoutant une annotation, vous pouvez faire en sorte qu’un attribut se comporte comme un compteur atomique implicite, conserver une valeur d’horodatage générée automatiquement ou suivre le numéro de version d’un article. Pour plus d’informations, consultez Annotations de classes de données.