Programmazione di DynamoDB con AWS SDK for Java 2.x - Amazon DynamoDB

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

Programmazione di DynamoDB con AWS SDK for Java 2.x

Questa guida alla programmazione fornisce un orientamento ai programmatori che desiderano utilizzare Amazon DynamoDB con Java. La guida tratta diversi concetti, tra cui i livelli di astrazione, la gestione della configurazione, la gestione degli errori, il controllo delle policy di ripetizione dei tentativi e la gestione del keep-alive.

Informazioni su AWS SDK for Java 2.x

È possibile accedere a DynamoDB da Java utilizzando il file ufficiale. AWS SDK per Java L’SDK per Java è disponibile in due versioni: 1.x e 2.x. La versione end-of-support 1.x è stata annunciata il 12 gennaio 2024. Entrerà in modalità manutenzione il 31 luglio 2024 e scadrà end-of-support il 31 dicembre 2025. Per le nuove implementazioni, si consiglia vivamente di utilizzare 2.x, che è stata rilasciata per la prima volta nel 2018. In questa guida viene trattata esclusivamente la versione 2.x e vengono illustrate solo le parti dell’SDK relative a DynamoDB.

Per informazioni sulla manutenzione e il supporto di AWS SDKs, consulta la politica di manutenzione di AWS SDK and Tools e la matrice di supporto della versione Tools nella AWS SDKs and Tools Reference AWS SDKs Guide.

AWS SDK for Java 2.x Si tratta di una riscrittura importante del codice base 1.x. L'SDK for Java 2.x supporta le moderne funzionalità Java, come la funzionalità I/O non bloccante introdotta in Java 8. L’SDK per Java 2.x aggiunge anche il supporto per implementazioni di client HTTP collegabili per fornire maggiore flessibilità di connessione di rete e più opzioni di configurazione.

Un cambiamento evidente tra l’SDK per Java 1.x e l’SDK per Java 2.x è l’uso di un nuovo nome pacchetto. L’SDK per Java 1.x utilizza il nome pacchetto com.amazonaws, mentre l’SDK per Java 2.x utilizza software.amazon.awssdk. Allo stesso modo, gli artefatti Maven per l’SDK per Java 1.x utilizzano il groupId com.amazonaws, mentre gli artefatti per l’SDK per Java 2.x utilizzano il groupId software.amazon.awssdk.

Importante

La versione AWS SDK per Java 1.x ha un pacchetto DynamoDB denominato. com.amazonaws.dynamodbv2 La dicitura “v2” nel nome pacchetto non indica che questo è riservato a Java 2 (J2SE). Piuttosto, “v2” indica che il pacchetto supporta la seconda versione dell’API di basso livello di DynamoDB anziché la versione originale dell’API di basso livello.

Support per le versioni Java

AWS SDK for Java 2.x Fornisce il supporto completo per le versioni Java con supporto a lungo termine (LTS).

Nozioni di base su AWS SDK for Java 2.x

Il seguente tutorial mostra come utilizzare Apache Maven per definire le dipendenze per SDK per Java 2.x. Questo tutorial mostra anche come scrivere il codice che si collega a DynamoDB per elencare le tabelle DynamoDB disponibili. Il tutorial contenuto in questa guida si basa sul tutorial Get started with the AWS SDK for Java 2.x nella AWS SDK for Java 2.x Developer Guide. Questo tutorial è stato modificato per effettuare chiamate a DynamoDB anziché ad Amazon S3.

Fase 1: prepararsi per il tutorial

Prima di iniziare questo tutorial, servirà quanto segue:

  • Autorizzazione ad accedere a DynamoDB.

  • Un ambiente di sviluppo Java configurato con accesso Single Sign-On all'utilizzo di Servizi AWS . Portale di accesso AWS

Per prepararti per questo tutorial, segui le istruzioni riportate in Setup overview nella AWS SDK for Java 2.x Developer Guide. Dopo aver configurato l’ambiente di sviluppo con l’accesso Single Sign-On per l’SDK per Java e aver svolto una sessione attiva sul portale di accesso AWS, continua con la Fase 2 di questo tutorial.

Fase 2: creare il progetto

Per creare il progetto per questo tutorial, si esegue un comando Maven che richiede input su come configurare il progetto. Dopo aver inserito e confermato tutti gli input, Maven completa la creazione del progetto creando un file pom.xml e file Java stub.

  1. Apri un terminale o una finestra del prompt dei comandi e accedi a una directory a tua scelta, ad esempio il Desktop o la cartella Home.

  2. Immetti il comando seguente sul terminale, quindi premi Invio.

    mvn archetype:generate \ -DarchetypeGroupId=software.amazon.awssdk \ -DarchetypeArtifactId=archetype-app-quickstart \ -DarchetypeVersion=2.22.0
  3. Per ogni prompt, inserisci il valore elencato nella seconda colonna.

    Prompt Valore da immettere
    Define value for property 'service': dynamodb
    Define value for property 'httpClient': apache-client
    Define value for property 'nativeImage': false
    Define value for property 'credentialProvider' identity-center
    Define value for property 'groupId': org.example
    Define value for property 'artifactId': getstarted
    Define value for property 'version' 1.0-SNAPSHOT: <Enter>
    Define value for property 'package' org.example: <Enter>
  4. Dopo aver inserito l’ultimo valore, Maven elenca le scelte effettuate. Per confermare, inserisci Y (per Yes, Sì). In alternativa, inserisci N (per No), quindi inserisci nuovamente le tue scelte.

Maven crea una cartella di progetto denominata getstarted in base al valore artifactId inserito. All’interno della cartella getstarted, cerca un file con nome README.md che è possibile esaminare, un file pom.xml e una directory src.

Maven crea la seguente struttura di cartelle.

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

Di seguito viene mostrato il contenuto del file di progetto pom.xml.

La sezione dependencyManagement contiene una dipendenza all’ AWS SDK for Java 2.x e la sezione dependencies ha una dipendenza per DynamoDB. La specificazione di queste dipendenze impone a Maven di includere i file .jar pertinenti nel percorso della classe Java. Per impostazione predefinita, l' AWS SDK non include tutte le classi per tutti. Servizi AWS Per DynamoDB, se si utilizza l’interfaccia di basso livello, dovrebbe essere presente una dipendenza sull’artefatto dynamodb. Oppure, se si utilizza l’interfaccia di alto livello, dovrebbe essere presente sull’artefatto dynamodb-enhanced. Se non vengono incluse le dipendenze pertinenti, non è possibile compilare il codice. Il progetto utilizza Java 1.8 a causa del valore 1.8 delle proprietà maven.compiler.source e 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>

Fase 3: scrivere il codice

Il codice seguente mostra la classe App creata da Maven. Il metodo main è il punto di ingresso nell’applicazione, che crea un’istanza della classe Handler e quindi ne richiama il metodo 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 creata da Maven contiene il metodo factory dynamoDbClient che crea e restituisce un’istanza DynamoDbClient. L’istanza DynamoDbClient utilizza un’istanza del client HTTP basato su Apache. Questo avviene perché si specifica apache-client quando Maven chiede quale client HTTP usare.

Il codice seguente mostra 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 contiene la logica principale del programma. Quando viene creata un’istanza di Handler nella classe App, DependencyFactory fornisce il servizio client DynamoDbClient. Il codice utilizza l’istanza DynamoDbClient per chiamare DynamoDB.

Maven genera la seguente classe Handler con un commento TODO. La fase successiva del tutorial sostituisce il commento TODO con il codice.

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

Per compilare la logica, sostituisci l’intero contenuto della classe Handler con il codice seguente. Il metodo sendRequest viene compilato e vengono aggiunte le importazioni necessarie.

Il codice seguente utilizza l’istanza DynamoDbClient per recuperare un elenco di tabelle esistenti. Se esistono tabelle per un determinato account e Regione AWS, il codice utilizza l’istanza Logger per registrare log dei nomi di queste tabelle.

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

Fase 4: creare ed eseguire l’applicazione

Dopo aver creato il progetto e nel momento in cui contiene la classe Handler completa, crea ed esegui l’applicazione.

  1. Assicurati di avere una AWS IAM Identity Center sessione attiva. Per confermare, esegui il comando AWS Command Line Interface (AWS CLI) aws sts get-caller-identity e controlla la risposta. Se non è presente una sessione attiva, consulta Sign in using the AWS CLI per istruzioni.

  2. Apri un terminale o una finestra del prompt dei comandi e vai alla directory del progetto getstarted.

  3. Pre compilare il progetto esegui il comando seguente:

    mvn clean package
  4. Per eseguire l’applicazione, esegui il comando seguente:

    mvn exec:java -Dexec.mainClass="org.example.App"

Dopo aver visualizzato il file, elimina l’oggetto e quindi elimina il bucket.

Riuscito

Se il progetto Maven è stato creato ed eseguito senza errori, congratulazioni! Hai creato con successo la tua prima applicazione Java utilizzando SDK per Java 2.x.

Pulizia

Per eliminare le risorse create durante questo tutorial, elimina la cartella del progetto getstarted.

Analisi della documentazione di AWS SDK for Java 2.x

La AWS SDK for Java 2.x Developer Guide copre tutti gli aspetti dell’SDK in tutti i Servizi AWS. Si consiglia di iniziare dagli argomenti seguenti:

  • Migrate from version 1.x to 2.x: include una spiegazione dettagliata delle differenze tra 1.x e 2.x. Questo argomento contiene anche istruzioni su come utilizzare entrambe le versioni principali side-by-side.

  • DynamoDB guide for Java 2.x SDK: mostra come eseguire le operazioni di base di DynamoDB: creazione di una tabella, manipolazione di elementi e recupero di elementi. Questi esempi utilizzano l’interfaccia di basso livello. Java ha diverse interfacce, come spiegato nella sezione seguente: Interfacce supportate.

Suggerimento

Dopo aver esaminato questi argomenti, aggiungi la documentazione di riferimento dell’API AWS SDK for Java 2.x ai segnalibri. Copre tutto Servizi AWS e ti consigliamo di utilizzarlo come riferimento API principale.

Interfacce supportate

AWS SDK for Java 2.x Supporta le seguenti interfacce, a seconda del livello di astrazione desiderato.

Interfaccia di basso livello

L'interfaccia di basso livello fornisce una one-to-one mappatura all'API di servizio sottostante. Ogni API DynamoDB è disponibile tramite questa interfaccia. Ciò significa che l’interfaccia di basso livello può fornire funzionalità complete, ma è spesso più verbosa e complessa da usare. Ad esempio, è necessario utilizzare le funzioni .s() per contenere le stringhe e le funzioni .n() per contenere i numeri. Il seguente esempio di PutIteminserimento di un elemento utilizzando l'interfaccia di basso livello.

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

Interfaccia di alto livello

L'interfaccia di alto livello di AWS SDK for Java 2.x si chiama DynamoDB Enhanced Client. Questa interfaccia offre un’esperienza di creazione del codice più idiomatica.

Il client avanzato offre una modalità di mappatura tra le classi di dati lato client e le tabelle DynamoDB progettate per archiviare tali dati. È possibile definire le relazioni tra le tabelle e le relative classi di modello corrispondenti nel codice. È quindi possibile fare affidamento sull’SDK per gestire la manipolazione dei tipi di dati. Per ulteriori informazioni sul client avanzato, consulta DynamoDB enhanced client API nella AWS SDK for Java 2.x Developer Guide.

L'esempio seguente PutItemutilizza l'interfaccia di alto livello. In questo esempio, DynamoDbBean con nome YourItem crea un file TableSchema che ne consente l’uso diretto come input per la chiamata 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; } } }

L' AWS SDK per Java 1.x ha una propria interfaccia di alto livello, a cui spesso si fa riferimento con la sua classe principale. DynamoDBMapper AWS SDK for Java 2.x È pubblicato in un pacchetto separato (e artefatto Maven) denominato. software.amazon.awssdk.enhanced.dynamodb L’SDK per Java 2.x viene spesso identificato tramite la sua classe principale DynamoDbEnhancedClient.

Interfaccia di alto livello con classi di dati immutabili

La funzionalità di mappatura fornita dall’API avanzata del client DynamoDB funziona anche con classi di dati immutabili. Una classe immutabile ha solo getter e richiede una classe builder che l’SDK utilizza per creare istanze della classe. L’immutabilità in Java è uno stile privo di effetti collaterali comunemente usato che gli sviluppatori possono utilizzare per creare classi. Queste classi hanno un comportamento più prevedibile in applicazioni multi-thread complesse. Invece di utilizzare l’annotazione @DynamoDbBean come mostrato nell’High-level interface example, le classi immutabili utilizzano l’annotazione @DynamoDbImmutable, che accetta la classe builder come input.

L’esempio seguente utilizza la classe builder DynamoDbEnhancedClientImmutablePutItem come input per creare uno schema della tabella. L'esempio fornisce quindi lo schema come input per la chiamata 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’esempio seguente mostra la classe di dati immutabili.

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

Interfaccia di alto livello che utilizza classi di dati immutabili e librerie di generazione di codice boilerplate di terze parti

Le classi di dati immutabili (mostrate nell’esempio precedente) richiedono codice boilerplate. Ad esempio, la logica di getter e setter sulle classi di dati, oltre alle classi Builder. Le librerie di terze parti, come Project Lombok, possono aiutare a generare quel tipo di codice boilerplate. La riduzione della maggior parte del codice standard consente di limitare la quantità di codice necessaria per lavorare con classi di dati immutabili e l'SDK. AWS Ciò determina inoltre una migliore produttività e leggibilità del codice. Per ulteriori informazioni, consulta Use third-party libraries, such as Lombok nella AWS SDK for Java 2.x Developer Guide.

L’esempio seguente mostra come Project Lombok semplifica il codice necessario per utilizzare l’API avanzata del client 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’esempio seguente mostra l’oggetto di dati immutabili della classe di dati immutabili.

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 YourImmutableLombokItem classe utilizza le seguenti annotazioni fornite da Project Lombok e dall'SDK: AWS

  • @Builder — Produce un generatore complesso APIs per le classi di dati fornito da Project Lombok.

  • @ DynamoDbImmutable — Identifica la DynamoDbImmutable classe come annotazione di entità mappabile DynamoDB fornita dall'SDK. AWS

  • @Value: la variante immutabile di @Data. Per impostazione predefinita, tutti i campi vengono resi privati e definitivi e i setter non vengono generati. Project Lombok fornisce questa annotazione.

Interfaccia documentale

L'interfaccia AWS SDK for Java 2.x Document evita la necessità di specificare i descrittori dei tipi di dati. I tipi di dati sono impliciti nella semantica dei dati stessi. Questa interfaccia Document è simile alla AWS SDK per Java 1.x, interfaccia Document, ma con un'interfaccia riprogettata.

Il seguente Document interface example illustra la chiamata PutItem espressa utilizzando l’interfaccia documentale. L'esempio utilizza anche. EnhancedDocument Per eseguire comandi su una tabella DynamoDB utilizzando l’API del documento avanzata, è necessario innanzitutto associare la tabella allo schema della tabella documentale per creare un oggetto risorsa DynamoDBTable. Il generatore di schemi della tabella documentale richiede la chiave dell’indice primario e i provider dei convertitori di attributi.

È possibile utilizzare AttributeConverterProvider.defaultProvider() per convertire gli attributi documentali di tipi predefiniti. È possibile modificare il comportamento predefinito generale con un’implementazione AttributeConverterProvider personalizzata. Inoltre, è possibile modificare il convertitore per un singolo attributo. La AWS SDKs and Tools Reference Guide fornisce maggiori dettagli ed esempi su come utilizzare i convertitori personalizzati. Il loro utilizzo principale è per gli attributi delle classi di dominio che non dispongono di un convertitore predefinito. Utilizzando un convertitore personalizzato, è possibile fornire all’SDK le informazioni necessarie per scrivere o leggere su 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)"); } }

Per convertire documenti JSON da e verso tipi di dati nativi di Amazon DynamoDB, è possibile utilizzare i seguenti metodi di utilizzo:

Confronto delle interfacce con un esempio Query

Questa sezione mostra la stessa chiamata Query espressa utilizzando le varie interfacce. Per ottimizzare i risultati di queste query, tieni presente quanto segue:

  • DynamoDB ha come destinazione un valore della chiave di partizione specifico, quindi è necessario specificare la chiave di partizione in modo completo.

  • Per fare in modo che la query abbia come destinazione solo gli articoli del carrello, la chiave di ordinamento contiene un’espressione di condizione della chiave che utilizza begins_with.

  • Si utilizza limit() per limitare la richiesta a un massimo di 100 elementi restituiti.

  • Si imposta il valore scanIndexForward su false. I risultati vengono restituiti in ordine di byte UTF-8, il che di solito significa che l’articolo nel carrello con il numero più basso viene restituito per primo. Impostando il valore scanIndexForward su false, si inverte l’ordine e l’articolo nel carrello con il numero più alto viene restituito per primo.

  • Si applica un filtro per rimuovere qualsiasi risultato che non corrisponde ai criteri. I dati filtrati consumano la capacità di lettura indipendentemente dal fatto che l’elemento corrisponda al filtro.

Esempio Query con l’interfaccia di basso livello

L’esempio seguente esegue la query di una tabella denominata YourTableName utilizzando una keyConditionExpression. Ciò limita la query a un valore specifico della chiave di partizione e a un valore della chiave di ordinamento che iniziano con un valore di prefisso specifico. Queste condizioni della chiave limitano la quantità di dati letti da DynamoDB. Infine, la query applica un filtro sui dati recuperati da DynamoDB utilizzando una 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")); } }
Esempio Query con l’interfaccia documentale

L’esempio seguente esegue la query di una tabella denominata YourTableName utilizzando un’interfaccia documentale.

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")); } }
Esempio Query con l’interfaccia di alto livello

L’esempio seguente esegue una query di una tabella denominata YourTableName utilizzando l’API avanzata del client 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; } } }
Interfaccia di alto livello con classi di dati immutabili

Quando si esegue un’operazione Query con le classi di dati immutabili di alto livello, il codice è lo stesso dell’esempio di interfaccia di alto livello, a eccezione della costruzione della classe di entità YourItem o YourImmutableItem. Per ulteriori informazioni, consulta l'PutItemesempio.

Interfaccia di alto livello che utilizza classi di dati immutabili e librerie di generazione di codice boilerplate di terze parti

Quando si esegue un’operazione Query con le classi di dati immutabili di alto livello, il codice è lo stesso dell’esempio di interfaccia di alto livello, a eccezione della costruzione della classe di entità YourItem o YourImmutableLombokItem. Per ulteriori informazioni, consulta l'PutItemesempio.

Ulteriori esempi di codice

Per ulteriori esempi di utilizzo di DynamoDB con l’SDK per Java 2.x, fai riferimento ai seguenti repository di esempi di codice:

Programmazione sincrona e asincrona

AWS SDK for Java 2.x Fornisce client sincroni e asincroni per, Servizi AWS come DynamoDB.

Le classi DynamoDbClient e DynamoDbEnhancedClient forniscono metodi sincroni che bloccano l’esecuzione del thread finché il client non riceve una risposta dal servizio. Questo client è il modo più semplice per interagire con DynamoDB se non sono necessarie operazioni asincrone.

Le classi DynamoDbAsyncClient e DynamoDbEnhancedAsyncClient forniscono metodi asincroni che terminano immediatamente, riassegnando il controllo al thread chiamante senza attendere una risposta. Il client senza blocchi ha un vantaggio che sfrutta per un’elevata simultaneità su pochi thread, il che consente una gestione efficiente delle richieste di I/O con risorse di calcolo minime. Ciò migliora il throughput e la reattività.

AWS SDK for Java 2.x Utilizza il supporto nativo per I/O non bloccanti. La versione 1.x doveva simulare I/O non bloccanti. AWS SDK per Java

I metodi asincroni terminano prima che sia disponibile una risposta, perciò occorre un modo per ottenere la risposta quando è pronta. I metodi asincroni in AWS SDK per Java restituiscono un CompletableFutureoggetto che contiene i risultati dell'operazione asincrona futura. Quando si chiamano query get() o join() su questi oggetti CompletableFuture, il codice si blocca finché il risultato non è disponibile. Se queste query vengono chiamate contemporaneamente alla richiesta, il comportamento è simile a una semplice chiamata sincrona.

Per ulteriori informazioni sulla programmazione asincrona, consulta Use asynchronous programming nella AWS SDK for Java 2.x Developer Guide.

Client HTTP

Per supportare ogni client, esiste un client HTTP che gestisce la comunicazione con i Servizi AWS. È possibile collegare client HTTP alternativi, scegliendone uno con le caratteristiche più adatte alla propria applicazione. Alcuni sono più leggeri, altri hanno più opzioni di configurazione.

Alcuni client HTTP supportano solo l’uso sincrono, mentre altri supportano solo l’uso asincrono. Per un diagramma di flusso che può aiutare a selezionare il client HTTP ottimale per uno specifico carico di lavoro, consulta HTTP client recommendations nella AWS SDK for Java 2.x Developer Guide.

L’elenco seguente presenta alcuni dei possibili client HTTP:

Client HTTP basato su Apache

La classe ApacheHttpClient supporta client di servizio sincroni. È il client HTTP predefinito per l’uso sincrono. Per informazioni sulla configurazione della classe ApacheHttpClient, consulta Configure the Apache-based HTTP client nella AWS SDK for Java 2.x Developer Guide.

Client HTTP basato su URLConnection

La classe UrlConnectionHttpClient è un’altra opzione per i client sincroni. Si carica più velocemente rispetto al client HTTP basato su Apache, ma ha meno funzionalità. Per informazioni sulla configurazione della UrlConnectionHttpClient classe, consulta Configurare il client HTTP URLConnection basato nella Guida per gli sviluppatori.AWS SDK for Java 2.x

Client HTTP basato su Netty

La classe NettyNioAsyncHttpClient supporta client asincroni. È la scelta predefinita per l’uso asincrono. Per informazioni sulla classe NettyNioAsyncHttpClient, consulta Configure the Netty-based HTTP client nella AWS SDK for Java 2.x Developer Guide.

AWS Client HTTP basato su CRT

Le nuove AwsCrtHttpClient e AwsCrtAsyncHttpClient le classi delle librerie AWS Common Runtime (CRT) sono altre opzioni che supportano client sincroni e asincroni. Rispetto ad altri client HTTP, CRT offre: AWS

  • Tempo di avvio del’SDK più rapido

  • Ingombro di memoria minore

  • Tempo di latenza ridotto

  • Gestione dell’integrità delle connessioni

  • Bilanciamento del carico DNS

Per informazioni sulla configurazione delle AwsCrtAsyncHttpClient classi AwsCrtHttpClient and, consultate Configurare i client HTTP AWS basati su CRT nella Guida per gli sviluppatori.AWS SDK for Java 2.x

Il client HTTP AWS basato su CRT non è l'impostazione predefinita perché ciò comprometterebbe la compatibilità con le versioni precedenti delle applicazioni esistenti. Tuttavia, per DynamoDB consigliamo di utilizzare il client HTTP basato su CRT sia per usi AWS sincronizzati che asincroni.

Per un'introduzione al client HTTP AWS basato su CRT, consulta Annuncio della disponibilità del client HTTP AWS CRT nel blog sugli strumenti per sviluppatori. AWS SDK for Java 2.xAWS

Configurazione di un client HTTP

Per la configurazione di un client, è possibile fornire diverse opzioni di configurazione, tra cui:

  • Impostazione dei timeout per diversi aspetti delle chiamate API.

  • Abilitazione di TCP Keep-Alive.

  • Controllo della policy di ripetizione dei tentativi in caso di errori.

  • Specifica degli attributi di esecuzione che le istanze degli strumenti di intercettazione dell’esecuzione possono modificare. Gli strumenti di intercettazione dell’esecuzione possono scrivere codice che intercetta l’esecuzione delle richieste e delle risposte dell’API. Ciò consente di eseguire attività come la pubblicazione di metriche e la modifica delle richieste in corso.

  • Aggiunta o manipolazione delle intestazioni HTTP.

  • Abilitazione del monitoraggio delle metriche delle prestazioni lato client. L'utilizzo di questa funzionalità ti aiuta a raccogliere metriche sui client di servizio nella tua applicazione e ad analizzare l'output in Amazon CloudWatch.

  • Specificazione di un servizio di esecuzione alternativo da utilizzare per la pianificazione delle attività, ad esempio la ripetizione dei tentativi e attività di timeout asincroni.

È possibile controllare la configurazione fornendo un oggetto ClientOverrideConfiguration alla classe Builder del client di servizio. Questo verrà riportato in alcuni esempi di codice nelle sezioni seguenti.

ClientOverrideConfiguration fornisce scelte di configurazione standard. I diversi client HTTP collegabili hanno anche possibilità di configurazione specifiche per l’implementazione.

Configurazione del timeout

È possibile regolare la configurazione del client per controllare vari timeout relativi alle chiamate del servizio. DynamoDB offre latenze inferiori rispetto ad altri Servizi AWS. Pertanto, si potrebbe voler modificare queste proprietà per ridurre i valori di timeout in modo da poter anticipare l’errore (fail fast) in caso di problemi di rete.

È possibile personalizzare il comportamento relativo alla latenza utilizzando ClientOverrideConfiguration sul client DynamoDB o modificando le opzioni di configurazione dettagliate sull’implementazione del client HTTP sottostante.

È possibile configurare le seguenti proprietà di impatto utilizzando ClientOverrideConfiguration:

  • apiCallAttemptTimeout: il tempo di attesa per il completamento di un singolo tentativo di completare una richiesta HTTP prima della rinuncia e dell’interruzione.

  • apiCallTimeout: il periodo di tempo a disposizione del client per eseguire completamente una chiamata API. Ciò include l’esecuzione del gestore delle richieste che consiste in tutte le richieste HTTP, incluse le ripetizioni di tentativi.

AWS SDK for Java 2.x Fornisce valori predefiniti per alcune opzioni di timeout, come il timeout della connessione e i timeout del socket. L’SDK non fornisce valori predefiniti per i timeout delle chiamate API o i timeout dei singoli tentativi di chiamata API. Se questi timeout non sono impostati in ClientOverrideConfiguration, l’SDK utilizza invece il valore di timeout del socket per il timeout complessivo delle chiamate API. La sessione ha un valore di timeout predefinito di 30 secondi.

RetryMode

Un’altra configurazione correlata alla configurazione del timeout da prendere in considerazione è l’oggetto di configurazione RetryMode. Questo oggetto di configurazione contiene una raccolta di comportamenti relativi alla ripetizione dei tentativi.

L’SDK per Java 2.x supporta le seguenti modalità di ripetizione dei tentativi:

  • legacy: la modalità di ripetizione dei tentativi predefinita se non la si modifica esplicitamente. Questa modalità di ripetizione dei tentativi è specifica dell’SDK per Java. È caratterizzata da un massimo di tre ripetizioni di tentativi, o più per servizi come DynamoDB, che prevede fino a otto ripetizioni.

  • standard— Chiamato «standard» perché è più coerente con gli altri. AWS SDKs Questa modalità attende un periodo di tempo casuale compreso tra 0 ms e 1.000 ms per la prima ripetizione di tentativo. Se è necessaria una nuova ripetizione di tentativo, questa modalità seleziona un altro periodo di tempo casuale da 0 ms a 1.000 ms e lo moltiplica per due. Se è necessaria una nuova ripetizione di tentativo, esegue la stessa scelta casuale moltiplicata per quattro e così via. Ogni attesa è limitata a 20 secondi. Questa modalità esegue ripetizioni di tentativi su un numero maggiore di condizioni di errore rilevate rispetto alla modalità legacy. Per DynamoDB, esegue fino a un massimo di tre tentativi, a meno che non si esegua la sostituzione con numRetries.

  • adaptive: si basa sulla modalità standard e limita dinamicamente la frequenza delle richieste AWS per massimizzare la percentuale di successo. Ciò può avvenire a scapito della latenza delle richieste. Non si consiglia la modalità adattiva per la ripetizione dei tentativi quando la latenza prevedibile è importante.

È possibile trovare una definizione estesa di queste modalità di ripetizione nell'argomento sul comportamento Riprova nella Guida di riferimento agli strumenti AWS SDKs e agli strumenti.

Policy di ripetizione dei tentativi

Tutte le configurazioni RetryMode hanno una RetryPolicy, che è costruita sulla base di una o più configurazioni RetryCondition. TokenBucketRetryCondition è particolarmente importante per il comportamento di ripetizione dei tentativi dell’implementazione del client SDK di DynamoDB. Questa condizione limita il numero di ripetizioni di tentativi che l’SDK effettua utilizzando un algoritmo token bucket. A seconda della modalità di ripetizione dei tentativi selezionata, le eccezioni di limitazione (della larghezza di banda della rete) possono o meno sottrarre token dal TokenBucket.

Quando un client rileva un errore per cui è possibile ripetere il tentativo, ad esempio un’eccezione di limitazione (della larghezza di banda della rete) o un errore temporaneo del server, l’SDK ritenta automaticamente la richiesta. È possibile controllare quante volte e con che velocità avviene la ripetizione dei tentativi.

Quando si configura un client, è possibile fornire un client RetryPolicy che supporti i seguenti parametri:

  • numRetries: il numero massimo di ripetizioni dei tentativi da applicare prima che una richiesta venga considerata non riuscita. Il valore predefinito è 8 indipendentemente dalla modalità di ripetizione dei tentativi utilizzata.

    avvertimento

    Assicurati di modificare questo valore predefinito dopo debita considerazione.

  • backoffStrategy: la BackoffStrategy da applicare alla ripetizione dei tentativi (FullJitterBackoffStrategy è la strategia predefinita). Questa strategia esegue un ritardo esponenziale tra ripetizioni di tentativi aggiuntive in base al numero di ripetizioni di tentativi correnti, un ritardo di base e un tempo massimo di backoff. Quindi aggiunge il jitter per avere della casualità. Il ritardo di base utilizzato nel ritardo esponenziale è di 25 ms indipendentemente dalla modalità di ripetizione dei tentativi.

  • retryCondition: RetryCondition determina se ripetere i tentativi o meno su una richiesta. Per impostazione predefinita, ripete i tentativi con un set specifico di codici di stato HTTP ed eccezioni per cui ritiene che possano essere effettuate ripetizioni di tentativi. Per la maggior parte delle situazioni, la configurazione predefinita dovrebbe essere sufficiente.

Il codice seguente fornisce una policy di ripetizione dei tentativi alternativa. Specifica un totale di cinque ripetizioni di tentativo (sei richieste totali). La prima ripetizione di tentativo deve avvenire dopo un ritardo di circa 100 ms e ogni ripetizione di tentativo aggiuntiva raddoppia tale tempo in modo esponenziale, fino a un ritardo massimo di un secondo.

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

Le proprietà di timeout che ClientOverrideConfiguration e RetryMode non gestiscono sono in genere configurate implicitamente specificando una DefaultsMode.

La AWS SDK for Java 2.x (versione 2.17.102 o successiva) ha introdotto il supporto per. DefaultsMode Questa funzionalità fornisce un set di valori predefiniti per le impostazioni configurabili più comuni, come le impostazioni di comunicazione HTTP, il comportamento della ripetizione dei tentativi, le impostazioni Regionali degli endpoint del servizio e potenzialmente qualsiasi configurazione relativa all’SDK. Quando si utilizza questa funzionalità, è possibile ottenere nuove impostazioni di configurazione predefinite personalizzate per scenari di utilizzo comuni.

Le modalità predefinite sono standardizzate in tutti i. AWS SDKs L’SDK per Java 2.x supporta le seguenti modalità predefinite:

  • legacy: fornisce impostazioni predefinite che variano in base all’SDK AWS e che esistevano prima della creazione di DefaultsMode.

  • standard: fornisce impostazioni predefinite non ottimizzate per la maggior parte degli scenari.

  • in-region— Si basa sulla modalità standard e include impostazioni personalizzate per le applicazioni che effettuano chiamate Servizi AWS dall'interno della stessa. Regione AWS

  • cross-region: si basa sulla modalità standard e include impostazioni con timeout elevati per le applicazioni che chiamano Servizi AWS in una Regione diversa.

  • mobile: si basa sulla modalità standard e include impostazioni con timeout elevati personalizzate per applicazioni mobili con latenze più elevate.

  • auto: si basa sulla modalità standard e include funzionalità sperimentali. L’SDK tenta di individuare l’ambiente di runtime per determinare automaticamente le impostazioni appropriate. Il rilevamento automatico è basato sull’euristica e non garantisce una precisione del 100%. Se non è possibile determinare l’ambiente di runtime, viene utilizzata la modalità standard. Il rilevamento automatico potrebbe eseguire query sui metadati e i dati utente dell’istanza, il che potrebbe introdurre latenza. Se la latenza di avvio è fondamentale per l’applicazione, si consiglia invece di scegliere una DefaultsMode esplicita.

È possibile configurare la modalità predefinita nei seguenti modi:

  • Direttamente su un client, tramite AwsClientBuilder.Builder#defaultsMode(DefaultsMode).

  • Su un profilo di configurazione, tramite la proprietà del file di profilo defaults_mode.

  • A livello globale, tramite la proprietà di sistema aws.defaultsMode.

  • A livello globale, tramite la variabile di ambiente AWS_DEFAULTS_MODE.

Nota

Per qualsiasi modalità diversa da legacy, i valori predefiniti forniti potrebbero cambiare man mano che le best practice si evolvono. Pertanto, in caso di utilizzo di una modalità diversa da legacy, si consiglia di eseguire dei test all’aggiornamento dell’SDK.

Le impostazioni predefinite di Smart nella AWS SDKs and Tools Reference Guide forniscono un elenco delle proprietà di configurazione e dei relativi valori predefiniti nelle diverse modalità predefinite.

Scegliete il valore della modalità predefinita in base alle caratteristiche dell'applicazione e al tipo con Servizio AWS cui l'applicazione interagisce.

Questi valori sono configurati tenendo conto di un'ampia gamma di Servizi AWS opzioni. Per un’implementazione tipica di DynamoDB in cui sia le tabelle che l’applicazione DynamoDB sono implementate in un’unica Regione, la modalità predefinita in-region è la più rilevante tra le modalità predefinite standard.

Esempio Configurazione del client SDK di DynamoDB ottimizzata per chiamate a bassa latenza

L’esempio seguente regola i timeout su valori inferiori per una chiamata DynamoDB con bassa latenza prevista.

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’implementazione individuale del client HTTP può fornire un controllo ancora più granulare sul timeout e sul comportamento di utilizzo della connessione. Ad esempio, per il client AWS basato su CRT, è possibile abilitareConnectionHealthConfiguration, che consente al client di monitorare attivamente lo stato delle connessioni utilizzate. Per ulteriori informazioni, consulta Configurazione avanzata dei client HTTP AWS basati su CRT nella Guida per gli sviluppatori.AWS SDK for Java 2.x

Configurazione Keep-Alive

L’abilitazione del keep-alive può ridurre le latenze riutilizzando le connessioni. Esistono due diversi tipi di keep-alive: HTTP Keep-Alive e TCP Keep-Alive.

  • HTTP Keep-Alive tenta di mantenere la connessione HTTPS tra il client e il server in modo che le richieste successive possano riutilizzare tale connessione. In questo modo si evita la pesante autenticazione HTTPS nelle richieste successive. HTTP Keep-Alive è abilitato per impostazione predefinita su tutti i client.

  • TCP Keep-Alive richiede che il sistema operativo sottostante invii piccoli pacchetti tramite la connessione del socket per fornire la garanzia aggiuntiva che il socket sia mantenuto attivo e per rilevare immediatamente eventuali interruzioni. Ciò garantisce che una richiesta successiva non perda tempo a cercare di utilizzare un socket interrotto. Per impostazione predefinita, TCP Keep-Alive è disabilitato su tutti i client. Gli esempi di codice seguenti mostrano come abilitarlo su ogni client HTTP. Se abilitato per tutti i client HTTP non basati su CRT, l’effettivo meccanismo di Keep-Alive dipende dal sistema operativo. Pertanto, è necessario configurare valori TCP Keep-Alive aggiuntivi, come il timeout e il numero di pacchetti, tramite il sistema operativo. È possibile farlo usando sysctl su Linux o macOS o i valori di registro su Windows.

Esempio per abilitare TCP Keep-Alive su un client HTTP basato su Apache
DynamoDbClient client = DynamoDbClient.builder() .httpClientBuilder(ApacheHttpClient.builder().tcpKeepAlive(true)) .build();
Client HTTP basato su URLConnection

Qualsiasi client sincrono che utilizza il client HTTP basato su URLConnection HttpURLConnection non dispone di un meccanismo per abilitare il keep-alive.

Esempio per abilitare TCP Keep-Alive su un client HTTP basato su Netty
DynamoDbAsyncClient client = DynamoDbAsyncClient.builder() .httpClientBuilder(NettyNioAsyncHttpClient.builder().tcpKeepAlive(true)) .build();
Esempio per abilitare TCP Keep-Alive su un client HTTP basato su CRT AWS

Con il client HTTP AWS basato su CRT, puoi abilitare TCP keep-alive e controllarne la durata.

DynamoDbClient client = DynamoDbClient.builder() .httpClientBuilder(AwsCrtHttpClient.builder() .tcpKeepAliveConfiguration(TcpKeepAliveConfiguration.builder() .keepAliveInterval(Duration.ofSeconds(50)) .keepAliveTimeout(Duration.ofSeconds(5)) .build())) .build();

Quando si utilizza il client DynamoDB asincrono, è possibile abilitare TCP Keep-Alive come illustrato nel codice seguente.

DynamoDbAsyncClient client = DynamoDbAsyncClient.builder() .httpClientBuilder(AwsCrtAsyncHttpClient.builder() .tcpKeepAliveConfiguration(TcpKeepAliveConfiguration.builder() .keepAliveInterval(Duration.ofSeconds(50)) .keepAliveTimeout(Duration.ofSeconds(5)) .build())) .build();

Gestione degli errori

Per quanto riguarda la gestione delle eccezioni, AWS SDK for Java 2.x utilizza eccezioni di runtime (non selezionate).

L’eccezione di base, che copre tutte le eccezioni dell’SDK, è SdkServiceException, che si estende da RuntimeException non controllata di Java. Se si intercetta questa eccezione, sarà possibile intercettare tutte le eccezioni generate dall’SDK.

SdkServiceException ha una sottoclasse denominata AwsServiceException. Questa sottoclasse indica qualsiasi problema di comunicazione con Servizio AWS. Ha una sottoclasse denominata DynamoDbException, che indica un problema nella comunicazione con DynamoDB. Se la si intercetta, sarà possibile intercettare tutte le eccezioni relative a DynamoDB, ma nessun’altra eccezione dell’SDK.

Sono disponibili tipi di eccezioni più specifici in DynamoDbException. Alcuni di questi tipi di eccezioni si applicano a operazioni sul piano di controllo (control-plane) come TableAlreadyExistsException. Altri si applicano alle operazioni sul piano dati. Di seguito è riportato un esempio di eccezione comune relativa al piano dati:

  • ConditionalCheckFailedException: si è specificata una condizione nella richiesta che ha restituito il valore false. Ad esempio, è possibile che si sia provato un aggiornamento condizionale su un elemento, ma il valore effettivo dell’attributo non corrispondesse al valore previsto nella condizione. Per una richiesta che non va a buon fine in questo modo non vengono effettuate ripetizioni di tentativi.

In altre situazioni non viene definita un’eccezione specifica. Ad esempio, quando le richieste vengono sottoposte a limitazione (della larghezza di banda della rete), potrebbe essere limitata la ProvisionedThroughputExceededException specifica, mentre in altri casi potrebbe essere limitata la DynamoDbException più generica. In entrambi i casi, è possibile determinare se la limitazione (della larghezza di banda della rete) ha causato l’eccezione controllando se isThrottlingException() restituisce true.

A seconda delle esigenze dell’applicazione, è possibile intercettare tutte le istanze AwsServiceException o DynamoDbException. Tuttavia, spesso è necessario un comportamento diverso in situazioni diverse. La logica utilizzata per gestire un errore nel controllo delle condizioni è diversa da quella per gestire la limitazione (della larghezza di banda della rete). Definisci quali percorsi eccezionali desideri gestire e assicurati di testare i percorsi alternativi. Questo aiuta ad assicurarsi di poter gestire tutti gli scenari pertinenti.

Per un elenco di errori comuni che si possono riscontrare, consulta Gestione degli errori con DynamoDB. Consulta anche Common Errors in Amazon DynamoDB API Reference. Il riferimento fornisce anche gli errori esatti possibili per ogni operazione API, ad esempio per l’operazione Query. Per informazioni sulla gestione delle eccezioni, consulta Exception handling for the AWS SDK for Java 2.x nella AWS SDK for Java 2.x Developer Guide.

AWS ID della richiesta

Ogni richiesta include un ID di richiesta: può essere utile recuperarlo se si sta lavorando con il Supporto AWS per diagnosticare un problema. Ogni eccezione derivata da SdkServiceException ha un metodo requestId() disponibile per recuperare l’ID della richiesta.

Registrazione dei log

L’utilizzo della registrazione di log fornita dall’SDK può essere utile sia per intercettare messaggi importanti dalle librerie client sia per scopi di debug più approfonditi. I logger sono gerarchici e l’SDK utilizza software.amazon.awssdk come logger root. È possibile configurare il livello con uno dei seguenti: TRACE, DEBUG, INFO, WARN, ERROR, ALL o OFF. Il livello configurato si applica a quel logger e verso il basso nella gerarchia dei logger.

Per la sua registrazione, AWS SDK for Java 2.x utilizza Simple Logging Façade for Java (J). SLF4 Questa funge da livello di astrazione rispetto agli altri logger e può essere usata per collegare il logger preferito. Per istruzioni su come collegare i logger, consultate il manuale utente J. SLF4

Ogni logger ha un comportamento particolare. Per impostazione predefinita, il logger Log4j 2.x crea un ConsoleAppender, che aggiunge gli eventi del log a System.out e i valori predefiniti al livello del log ERROR.

Il SimpleLogger logger incluso in SLF4 J emette per impostazione predefinita su System.err e imposta di default il livello di log. INFO

Si consiglia di impostare il livello su WARN per software.amazon.awssdk affinché qualsiasi implementazione di produzione intercetti eventuali messaggi importanti dalle librerie client dell’SDK limitando al contempo la quantità di output.

Se SLF4 J non riesce a trovare un logger supportato nel percorso della classe (nessun binding SLF4 J), per impostazione predefinita è un'implementazione senza operazioni. Questa implementazione comporta la registrazione di messaggi che System.err spiegano che non sono riuscito a trovare un' SLF4implementazione del logger nel classpath. Per evitare questa situazione, è necessario aggiungere un’implementazione del logger. Per fare ciò, è possibile aggiungere una dipendenza in Apache Maven pom.xml verso gli artefatti, come org.slf4j.slf4j-simple o org.apache.logging.log4j.log4j-slf4j2-imp.

Per informazioni su come configurare la registrazione di log nell’SDK, compresa l’aggiunta di dipendenze di registrazione di log alla configurazione dell’applicazione, consulta Logging with the SDK for Java 2.x in the AWS SDK per Java Developer Guide.

La seguente configurazione nel file Log4j2.xml mostra come regolare il comportamento di registrazione di log se si utilizza il logger Apache Log4j 2. Questa configurazione imposta il livello del logger root su WARN. Tutti i logger della gerarchia ereditano questo livello di log, incluso il logger software.amazon.awssdk.

Per impostazione predefinita, l’output viene inviato a System.out. Nell’esempio seguente si sostituisce comunque l’appender di output predefinito Log4j per applicare un Log4j personalizzato PatternLayout.

Esempio di un file di configurazione Log4j2.xml

La seguente configurazione registra log di messaggi sulla console ai livelli ERROR e WARN per tutte le gerarchie di logger.

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

AWS richiedere la registrazione degli ID

Quando qualcosa va storto, puoi trovare la richiesta IDs tra le eccezioni. Tuttavia, se desideri la richiesta IDs per le richieste che non generano eccezioni, puoi utilizzare la registrazione.

Il software.amazon.awssdk.request logger emette la richiesta IDs a livello. DEBUG L’esempio seguente estende il precedente configuration example per mantenere il livello del logger root al livello ERROR, del software.amazon.awssdk al livello WARN e del software.amazon.awssdk.request al livello DEBUG. L'impostazione di questi livelli aiuta a catturare la richiesta IDs e altri dettagli relativi alla richiesta, come l'endpoint e il codice di stato.

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

Di seguito è riportato un esempio di output del log:

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

Paginazione

Alcune richieste, come Query e Scan, limitano la dimensione dei dati restituiti in una singola richiesta e richiedono l’esecuzione di richieste ripetute per richiamare le pagine successive.

È possibile controllare il numero massimo di elementi da leggere per ogni pagina con il parametro Limit. Ad esempio, è possibile utilizzare il parametro Limit per recuperare solo gli ultimi 10 elementi. Questo limite specifica il numero di elementi da leggere dalla tabella prima di applicare qualsiasi filtro. Se si desiderano esattamente 10 elementi dopo il filtraggio, non c’è modo di specificarlo. È possibile controllare solo il conteggio prefiltrato e controllare lato client quando sono stati effettivamente recuperati 10 elementi. Indipendentemente dal limite, le risposte hanno sempre una dimensione massima di 1 MB.

Nella risposta dell’API potrebbe essere inclusa una LastEvaluatedKey. Ciò indica che la risposta è terminata perché ha raggiunto un limite di conteggio o di dimensione. Questa chiave è l’ultima chiave valutata per quella risposta. Interagendo direttamente con l’API, è possibile recuperare questa LastEvaluatedKey e passarla a una chiamata successiva come ExclusiveStartKey in modo da leggere il blocco successivo da quel punto di partenza. Se non viene restituita alcuna LastEvaluatedKey, significa che non ci sono più elementi che corrispondono alla chiamata API Query o Scan.

L’esempio seguente utilizza l’interfaccia di basso livello per limitare gli elementi a 100 in base al parametro 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()); }

AWS SDK for Java 2.x Possono semplificare questa interazione con DynamoDB fornendo metodi di impaginazione automatica che effettuano più chiamate di servizio per ottenere automaticamente le pagine successive di risultati. Questo semplifica il codice, ma toglie un certo controllo sull’utilizzo delle risorse che si avrebbe con la lettura manuale delle pagine.

Utilizzando i metodi Iterable disponibili nel client DynamoDB, come QueryPaginator e ScanPaginator, l’SDK si occupa dell’impaginazione. Il tipo restituito da questi metodi è un iterabile personalizzato che è possibile utilizzare per iterare tutte le pagine. L’SDK si occupa di gestire internamente le chiamate del servizio. Utilizzando l’API per i flussi Java, è possibile gestire il risultato di QueryPaginator come mostrato nell’esempio seguente.

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

Annotazioni sulle classi di dati

L’SDK per Java fornisce diverse annotazioni che è possibile inserire negli attributi della classe di dati. Queste annotazioni influenzano il modo in cui l’SDK interagisce con gli attributi. Aggiungendo un’annotazione, è possibile fare in modo che un attributo si comporti come un contatore atomico implicito, mantenga un valore di timestamp generato automaticamente o tenga traccia del numero di versione di un elemento. Per ulteriori informazioni, consulta Data class annotations.