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 Amazon DynamoDB con Python e Boto3
Questa guida fornisce un orientamento ai programmatori che desiderano utilizzare Amazon DynamoDB con Python. La guida copre i diversi livelli di astrazione, la gestione della configurazione, la gestione degli errori, il controllo delle policy di ripetizione dei tentativi, la gestione del keep-alive e altro ancora.
Argomenti
Informazioni su Boto
Puoi accedere a DynamoDB da Python utilizzando l' AWS SDK ufficiale per Python, comunemente chiamato Boto3. Il nome Boto deriva da un delfino d’acqua dolce originario del Rio delle Amazzoni. La libreria Boto3 è la terza versione principale della libreria, rilasciata per la prima volta nel 2015. La libreria Boto3 è piuttosto ampia, in quanto supporta tutti i servizi, non solo DynamoDB. AWS Questa guida si rivolge solo alle parti di Boto3 rilevanti per DynamoDB.
Boto è gestito e pubblicato da un progetto open source ospitato AWS su. GitHub È suddiviso in due pacchetti: Botocore
-
Botocore fornisce funzionalità di basso livello. In Botocore si trovano il client, la sessione, le credenziali, la configurazione e le classi di eccezione.
-
Boto3 si basa su Botocore. Offre un’interfaccia di livello superiore più in linea con Python. In particolare, presenta una tabella DynamoDB come risorsa e offre un’interfaccia più semplice ed elegante rispetto all’interfaccia client di livello inferiore e orientata ai servizi.
Poiché questi progetti sono ospitati su GitHub, puoi visualizzare il codice sorgente, tenere traccia dei problemi aperti o inviare i tuoi problemi.
Utilizzo della documentazione Boto
Inizia a esplorare la documentazione Boto con le seguenti risorse:
-
Inizia con la sezione Quickstart
che costituisce un valido punto di partenza per l’installazione del pacchetto. Se non lo è già, vai lì per istruzioni su come installare Boto3 (Boto3 è spesso disponibile automaticamente all'interno di AWS servizi come). AWS Lambda -
Dopodiché, concentrati sulla guida a DynamoDB
della documentazione. La guida mostra come eseguire le attività di base di DynamoDB: creare ed eliminare una tabella, manipolare elementi, eseguire operazioni batch, eseguire una query ed eseguire una scansione. Gli esempi utilizzano l’interfaccia delle risorse. boto3.resource('dynamodb')indica che si sta utilizzando l’interfaccia delle risorse di livello superiore. -
Dopo la guida, è possibile consultare la documentazione di riferimento di DynamoDB
. Questa landing page fornisce un elenco esaustivo delle classi e dei metodi disponibili. In alto viene mostrata la classe DynamoDB.Client. Questa fornisce un accesso di basso livello a tutte le operazioni del piano di controllo (control-plane) e del piano dati. In basso si trova la classeDynamoDB.ServiceResource. Questa è l’interfaccia di livello superiore, in linea con Python. Con essa è possibile creare una tabella, eseguire operazioni batch tra tabelle o ottenere un’istanzaDynamoDB.ServiceResource.Tableper azioni specifiche della tabella.
Informazioni sui livelli di astrazione del client e delle risorse
Le due interfacce di lavoro sono l’interfaccia del client e l’interfaccia delle risorse.
-
L’interfaccia del client di basso livello fornisce una mappatura uno a uno con l’API del servizio sottostante. Tutte le API offerte da DynamoDB sono disponibili tramite il client. Ciò significa che l’interfaccia del client può fornire funzionalità complete, ma è spesso più verbosa e complessa da usare.
-
L’interfaccia delle risorse di livello superiore non fornisce una mappatura uno a uno dell’API del servizio sottostante. Tuttavia, fornisce metodi che semplificano l’accesso al servizio, ad esempio
batch_writer.
Ecco un esempio di inserimento di un elemento utilizzando l’interfaccia del client. Tutti i valori vengono passati come mappa con la chiave che ne indica il tipo (“S” per stringa, “N” per numero) e il loro valore come stringa. Questo approccio è noto come formato JSON DynamoDB.
import boto3 dynamodb = boto3.client('dynamodb') dynamodb.put_item( TableName='YourTableName', Item={ 'pk': {'S': 'id#1'}, 'sk': {'S': 'cart#123'}, 'name': {'S': 'SomeName'}, 'inventory': {'N': '500'}, # ... more attributes ... } )
Ecco la stessa operazione PutItem utilizzando l’interfaccia delle risorse. La tipizzazione dei dati è implicita:
import boto3 dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') table.put_item( Item={ 'pk': 'id#1', 'sk': 'cart#123', 'name': 'SomeName', 'inventory': 500, # ... more attributes ... } )
Se necessario, è possibile eseguire la conversione tra JSON normale e JSON DynamoDB utilizzando le classi TypeSerializer e TypeDeserializer fornite con boto3:
def dynamo_to_python(dynamo_object: dict) -> dict: deserializer = TypeDeserializer() return { k: deserializer.deserialize(v) for k, v in dynamo_object.items() } def python_to_dynamo(python_object: dict) -> dict: serializer = TypeSerializer() return { k: serializer.serialize(v) for k, v in python_object.items() }
Ecco come eseguire una query utilizzando l’interfaccia del client. Esprime la query come un costrutto JSON. Utilizza una stringa KeyConditionExpression che richiede la sostituzione di variabili per gestire eventuali conflitti tra parole chiave:
import boto3 client = boto3.client('dynamodb') # Construct the query response = client.query( TableName='YourTableName', KeyConditionExpression='pk = :pk_val AND begins_with(sk, :sk_val)', FilterExpression='#name = :name_val', ExpressionAttributeValues={ ':pk_val': {'S': 'id#1'}, ':sk_val': {'S': 'cart#'}, ':name_val': {'S': 'SomeName'}, }, ExpressionAttributeNames={ '#name': 'name', } )
La stessa operazione di query utilizzando l’interfaccia delle risorse può essere abbreviata e semplificata:
import boto3 from boto3.dynamodb.conditions import Key, Attr dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') response = table.query( KeyConditionExpression=Key('pk').eq('id#1') & Key('sk').begins_with('cart#'), FilterExpression=Attr('name').eq('SomeName') )
Come ultimo esempio, si immagini di voler ottenere la dimensione approssimativa di una tabella (ossia i metadati conservati nella tabella che viene aggiornata ogni 6 ore circa). Con l’interfaccia del client, si esegue un’operazione describe_table() e si estrae la risposta dalla struttura JSON restituita:
import boto3 dynamodb = boto3.client('dynamodb') response = dynamodb.describe_table(TableName='YourTableName') size = response['Table']['TableSizeBytes']
Con l’interfaccia delle risorse, la tabella esegue l’operazione di descrizione in modo implicito e presenta i dati direttamente come attributo:
import boto3 dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') size = table.table_size_bytes
Nota
Quando valutate se sviluppare utilizzando il client o l'interfaccia delle risorse, tenete presente che non verranno aggiunte nuove funzionalità all'interfaccia delle risorse come indicato nella documentazione delle risorse
Utilizzo della risorsa della tabella batch_writer
Una comodità disponibile solo con la risorsa della tabella di livello superiore è batch_writer. DynamoDB supporta operazioni di scrittura in batch che consentono fino a 25 operazioni di inserimento o eliminazione in una richiesta di rete. Questo tipo di operazioni in batch migliora l’efficienza riducendo al minimo i round trip di rete.
Con la libreria client di basso livello è possibile utilizzare l’operazione client.batch_write_item() per eseguire batch. È necessario suddividere manualmente il lavoro in batch da 25. Dopo ogni operazione, è inoltre necessario richiedere di ricevere un elenco di elementi non elaborati (alcune operazioni di scrittura potrebbero avere esito positivo mentre altre potrebbero fallire). È quindi necessario passare nuovamente gli elementi non elaborati a un’operazione batch_write_item() successivamente. È presente una quantità significativa di codice boilerplate.
Il metodo
dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') movies = # long list of movies in {'pk': 'val', 'sk': 'val', etc} format with table.batch_writer() as writer: for movie in movies: writer.put_item(Item=movie)
Ulteriori esempi di codice che esplorano i livelli del client e delle risorse
È possibile anche fare riferimento ai seguenti repository di esempi di codice che esplorano l’utilizzo delle varie funzioni, utilizzando sia client che risorse:
Informazioni sull’interazione degli oggetti Client e Resource con sessioni e thread
-
Un client è generalmente thread-safe una volta creato, a parte alcune funzionalità avanzate. Puoi creare un client e richiamarlo da più thread contemporaneamente. Per ulteriori informazioni su queste eccezioni, consulta la guida sui client
nella documentazione di Boto3. -
Una risorsa non è thread-safe. Un singolo oggetto Resource non deve essere utilizzato da più di un thread. Per ulteriori informazioni, consulta la guida sulle risorse
nella documentazione di Boto3. -
Una sessione non è thread-safe. Il rischio consiste nel creare clienti o risorse a partire da essa: due thread che li generano contemporaneamente dalla stessa sessione possono causare risposte non funzionanti o arresti anomali nel livello SSL sottostante. Crearli uno alla volta da una sessione va bene. Per ulteriori informazioni, consulta la guida sulle sessioni
nella documentazione di Boto3.
Se non crei la tua sessione boto3.resource() e boto3.client() usi una sessione predefinita condivisa. Questo va bene nel codice a thread singolo, ma nel codice a più thread ogni thread crea oggetti dalla stessa sessione, che è il modello non sicuro descritto in precedenza.
Condividi un client tra i thread
Poiché un cliente è thread-safe, l'approccio più semplice ed efficiente è creare un singolo client nel thread principale e consegnarlo a tutti i lavoratori. Condividono tutti il suo unico pool di connessioni.
Creato una volta, nel thread principale, quindi condiviso con tutti i thread:
session = boto3.Session() client = session.client("dynamodb")
Assegna a ogni thread la propria risorsa
Poiché una risorsa non è thread-safe, ogni thread ha bisogno della propria. Crea una nuova sessione all'interno del thread e crea la risorsa da essa, in modo che due thread non generino risorse dalla stessa sessione contemporaneamente.
Viene eseguito all'interno di ogni thread:
session = boto3.Session() dynamodb = session.resource("dynamodb")
Personalizzazione dell’oggetto Config
Quando si costruisce un oggetto Client o Resource, è possibile passare parametri denominati opzionali per personalizzare il comportamento. Il parametro denominato config sblocca una serie di funzionalità. È un’istanza di botocore.client.Config e la documentazione di riferimento per Config
Nota
È possibile modificare molte di queste impostazioni comportamentali a livello di Session, all’interno del file di configurazione AWS o come variabili di ambiente.
Configurazione per i timeout
Un uso di una configurazione personalizzata è quello di regolare i comportamenti di rete:
-
connect_timeout (float o int): il tempo in secondi prima che venga generata un’eccezione di timeout quando si tenta di stabilire una connessione. Il valore predefinito è 60 secondi.
-
read_timeout (float o int): il tempo in secondi prima che venga generata un’eccezione di timeout quando si tenta di leggere da una connessione. Il valore predefinito è 60 secondi.
I timeout di 60 secondi sono eccessivi per DynamoDB. Ciò significa che un problema temporaneo della rete causerà un minuto di ritardo per il client prima di poter riprovare. Il codice seguente riduce i timeout a 10 secondi:
import boto3 from botocore.config import Config my_config = Config( connect_timeout = 10.0, read_timeout = 10.0 ) dynamodb = boto3.resource('dynamodb', config=my_config)
Per ulteriori informazioni sui timeout, consulta Ottimizzazione delle impostazioni di richiesta HTTP di AWS Java SDK per applicazioni DynamoDB sensibili alla latenza.
Configurazione per keep-alive
Se usi botocore 1.27.84 o versione successiva, puoi anche controllare il protocollo TCP: Keep-Alive
-
tcp_keepalive (bool) - Abilita l'opzione Keep-Alive socket TCP utilizzata durante la creazione di nuove connessioni se impostata su (il valore predefinito è).
TrueFalseDisponibile solo a partire da botocore 1.27.84.
L'impostazione di TCP su può ridurre le latenze medie. Keep-Alive True Ecco un esempio di codice che imposta condizionalmente TCP Keep-Alive su true quando si dispone della versione botocore corretta:
import botocore import boto3 from botocore.config import Config required_version = "1.27.84" current_version = botocore.__version__ my_config = Config( connect_timeout = 0.5, read_timeout = 0.5 ) if tuple(map(int, current_version.split("."))) >= tuple(map(int, required_version.split("."))): my_config = my_config.merge(Config(tcp_keepalive = True)) dynamodb = boto3.resource('dynamodb', config=my_config)
Nota
TCP è diverso da HTTP Keep-Alive . Keep-Alive Con TCP Keep-Alive, piccoli pacchetti vengono inviati dal sistema operativo sottostante tramite la connessione socket per mantenere attiva la connessione e rilevare immediatamente eventuali interruzioni. Con HTTP Keep-Alive, la connessione web creata sul socket sottostante viene riutilizzata. Keep-Alive L'HTTP è sempre abilitato con boto3.
Esiste un limite per la quantità di tempo in cui una connessione inattiva può essere mantenuta attiva. Prendi in considerazione l’invio di richieste periodiche, ad esempio ogni minuto, se hai una connessione inattiva ma desideri che la richiesta successiva utilizzi una connessione già stabilita.
Configurazione per le ripetizioni di tentativi
La configurazione accetta anche un dizionario denominato Nuovi tentativi in cui è possibile specificare il comportamento desiderato per la ripetizione dei tentativi. Le ripetizioni di tentativi avvengono all’interno dell’SDK quando l’SDK riceve un errore e l’errore è di tipo transitorio. Se dopo un errore viene effettuata una ripetizione di tentativo internamente, successivamente seguita da una risposta positiva), dal punto di vista del codice non c’è alcun errore, ma solo una latenza leggermente maggiore. Ecco i valori che è possibile specificare:
-
max_attempts: un numero intero che rappresenta il numero massimo di ripetizioni dei tentativi che verranno effettuate su una singola richiesta. Ad esempio, impostando questo valore su 2, la richiesta verrà ritentata al massimo due volte dopo la richiesta iniziale. L’impostazione di questo valore su 0 non comporterà alcuna ripetizione dei tentativi dopo la richiesta iniziale.
-
total_max_attempts: un numero intero che rappresenta il numero massimo di tentativi totali che verranno effettuati su una singola richiesta. Ciò include la richiesta iniziale, quindi il valore 1 indica che per nessuna richiesta verranno effettuate ripetizioni di tentativi. Se
total_max_attemptsemax_attemptssono entrambi forniti,total_max_attemptsha la precedenza.total_max_attemptsè preferito rispetto amax_attemptsperché è mappato sulla variabile di ambienteAWS_MAX_ATTEMPTSe sul valore del file di configurazionemax_attempts. -
mode: una stringa che rappresenta il tipo di modalità di ripetizione dei tentativi che botocore dovrebbe usare. I valori validi sono:
-
legacy: la modalità predefinita. Attende 50 ms alla prima ripetizione di tentativo, quindi utilizza un backoff esponenziale con un fattore base pari a 2. Per DynamoDB, esegue fino a un massimo di 10 tentativi totali, a meno che non si esegua la sostituzione con quanto sopra).
Nota
Con il backoff esponenziale, l’ultimo tentativo attenderà quasi 13 secondi.
-
standard: denominato standard perché è più coerente con altri AWS SDK. Attende un periodo di tempo casuale compreso tra 0 ms e 1.000 ms per la prima ripetizione del tentativo. Se è necessaria una nuova ripetizione del tentativo, seleziona un altro periodo di tempo casuale tra 0 ms e 1.000 ms e lo moltiplica per due. Se è necessaria una nuova ripetizione del tentativo, esegue la stessa scelta casuale moltiplicata per quattro e così via. Ogni attesa è limitata a 20 secondi. Questa modalità eseguirà ripetizioni dei tentativi su un numero maggiore di condizioni di errore rilevate rispetto alla modalità
legacy. Per DynamoDB, esegue fino a un massimo di tre tentativi totali, a meno che non si esegua la sostituzione con quanto sopra). -
adattiva: una modalità di ripetizione dei tentativi sperimentale che include tutte le funzionalità della modalità standard ma aggiunge la limitazione (della larghezza di banda della rete) automatica lato client. Grazie alla limitazione adattiva della velocità, gli SDK possono rallentare la velocità di invio delle richieste per soddisfare meglio la capacità dei servizi. AWS Si tratta di una modalità provvisoria il cui comportamento potrebbe cambiare.
-
Una definizione estesa di queste modalità di ripetizione dei tentativi è disponibile nella guida alla ripetizione dei tentativi
Ecco un esempio che utilizza esplicitamente la policy di ripetizione dei tentativi legacy con un massimo di tre richieste totali (due ripetizioni di tentativi):
import boto3 from botocore.config import Config my_config = Config( connect_timeout = 1.0, read_timeout = 1.0, retries = { 'mode': 'legacy', 'total_max_attempts': 3 } ) dynamodb = boto3.resource('dynamodb', config=my_config)
Poiché DynamoDB è un sistema ad alta disponibilità e a bassa latenza, potresti voler essere più aggressivo con la velocità dei tentativi rispetto a quella consentita dalle politiche di ripetizione integrate. È possibile implementare la propria policy di ripetizione dei tentativi impostando il numero massimo di tentativi su 0, individuando personalmente le eccezioni ed effettuando ripetizioni dei tentativi, se necessario, utilizzando il proprio codice invece di affidarsi a boto3 per eseguire ripetizioni di tentativi implicite.
Se si gestisce in autonomia la propria policy sulla ripetizione dei tentativi, si consiglia di distinguere tra limitazioni (della larghezza di banda della rete) ed errori:
-
Una limitazione (della larghezza di banda della rete) (indicata da
ProvisionedThroughputExceededExceptionoThrottlingException) indica un servizio funzionante che informa che è stata superata la capacità di lettura o scrittura su una tabella o partizione DynamoDB. Ogni millisecondo che passa viene resa disponibile altra capacità di lettura o scrittura, così che sia possibile effettuare ripetizioni di tentativi rapidamente, ad esempio ogni 50 ms, per tentare di accedere alla capacità appena rilasciata. Con le limitazioni (della larghezza di banda della rete) non è particolarmente necessario un backoff esponenziale, perché queste limitazioni sono leggere da restituire per DynamoDB e non comportano alcun costo per richiesta. Il backoff esponenziale assegna ritardi più lunghi ai thread client che hanno già atteso più a lungo, il che determina un aumento statistico di p50 e p99. -
Un errore (indicato da
InternalServerErroroServiceUnavailable, tra gli altri) indica un problema temporaneo con il servizio. Questo può riguardare l’intera tabella o eventualmente solo la partizione da cui si sta leggendo o su cui si sta scrivendo. In caso di errori, è possibile sospendere più a lungo prima della ripetizione dei tentativi, ad esempio 250 ms o 500 ms, e utilizzare il jitter per scaglionare le ripetizioni di tentativi.
Per informazioni sulle modifiche imminenti al comportamento di ripetizione predefinito in tutti gli SDK, consulta Annuncio del comportamento di riprova aggiornato per AWS
SDK and Tools nel Developer Tools Blog. AWS
Configurazione per il numero massimo di connessioni nel pool
Infine, la configurazione consente di controllare la dimensione del pool di connessioni:
-
max_pool_connections (int): il numero massimo di connessioni da mantenere in un pool di connessioni. Se non viene impostato alcun valore, viene utilizzato il valore predefinito 10.
Questa opzione controlla il numero massimo di connessioni HTTP da conservare in pool per il riutilizzo. Viene creato un pool diverso per ogni cliente. Se un thread su un Client desidera una connessione e il pool è vuoto, effettuerà una nuova connessione. Se un thread tenta di restituire una connessione al pool dopo l'uso e trova il pool pieno, chiuderà la connessione. Se prevedi che più di 10 thread siano indirizzati a un singolo client, valuta la possibilità di aumentare le dimensioni del pool in modo che corrispondano al numero di thread.
import boto3 from botocore.config import Config my_config = Config( max_pool_connections = 20 ) session = boto3.Session() # One client with a larger connection pool client = session.client("dynamodb", config=my_config)
Ogni risorsa ha anche un pool di connessioni all'interno, ma un oggetto Resource non è thread-safe, quindi non condividerne uno tra i thread. Assegna a ogni thread la propria risorsa.
Gestione degli errori
AWS le eccezioni di servizio non sono tutte definite staticamente in Boto3. Questo perché gli errori e le eccezioni AWS dei servizi variano notevolmente e sono soggetti a modifiche. Boto3 racchiude tutte le eccezioni di servizio come ClientError ed espone i dettagli come JSON strutturato. Ad esempio, una risposta di errore potrebbe essere strutturata in questo modo:
{ 'Error': { 'Code': 'SomeServiceException', 'Message': 'Details/context around the exception or error' }, 'ResponseMetadata': { 'RequestId': '1234567890ABCDEF', 'HostId': 'host ID data will appear here as a hash', 'HTTPStatusCode': 400, 'HTTPHeaders': {'header metadata key/values will appear here'}, 'RetryAttempts': 0 } }
Il codice seguente rileva tutte eccezioni ClientError e analizza il valore della stringa di Code all’interno di Error per determinare l’azione da intraprendere:
import botocore import boto3 dynamodb = boto3.client('dynamodb') try: response = dynamodb.put_item(...) except botocore.exceptions.ClientError as err: print('Error Code: {}'.format(err.response['Error']['Code'])) print('Error Message: {}'.format(err.response['Error']['Message'])) print('Http Code: {}'.format(err.response['ResponseMetadata']['HTTPStatusCode'])) print('Request ID: {}'.format(err.response['ResponseMetadata']['RequestId'])) if err.response['Error']['Code'] in ('ProvisionedThroughputExceededException', 'ThrottlingException'): print("Received a throttle") elif err.response['Error']['Code'] == 'InternalServerError': print("Received a server error") else: raise err
Alcuni (ma non tutti) i codici di eccezione sono stati materializzati come classi di primo livello. È possibile scegliere di gestirli direttamente. Quando si utilizza l’interfaccia Client, queste eccezioni vengono popolate dinamicamente sul client ed è possibile intercettarle utilizzando l’istanza client in questo modo:
except ddb_client.exceptions.ProvisionedThroughputExceededException:
Quando si utilizza l’interfaccia Resource, è necessario utilizzare .meta.client per passare dalla risorsa al Client sottostante per accedere alle eccezioni, in questo modo:
except ddb_resource.meta.client.exceptions.ProvisionedThroughputExceededException:
Per esaminare l’elenco dei tipi di eccezioni materializzate, è possibile generarlo dinamicamente:
ddb = boto3.client("dynamodb") print([e for e in dir(ddb.exceptions) if e.endswith('Exception') or e.endswith('Error')])
Quando si esegue un’operazione di scrittura con un’espressione condizionale, è possibile richiedere che, se l’espressione non va a buon fine, il valore dell’elemento venga restituito nella risposta di errore.
try: response = table.put_item( Item=item, ConditionExpression='attribute_not_exists(pk)', ReturnValuesOnConditionCheckFailure='ALL_OLD' ) except table.meta.client.exceptions.ConditionalCheckFailedException as e: print('Item already exists:', e.response['Item'])
Per ulteriori informazioni sulla gestione degli errori e sulle eccezioni:
-
La guida di boto3 sulla gestione degli errori
contiene ulteriori informazioni sulle tecniche di gestione degli errori. -
La sezione della guida per sviluppatori di DynamoDB sugli errori di programmazione elenca gli errori che si possono riscontrare.
-
La sezione sugli errori comuni nella documentazione di riferimento dell’API.
-
La documentazione su ciascuna operazione API elenca gli errori che una chiamata potrebbe generare (ad esempio BatchWriteItem).
Registrazione dei log
La libreria boto3 si integra con il modulo di registrazione di log integrato di Python per tracciare ciò che accade durante una sessione. Per controllare i livelli di registrazione di log, è possibile configurare il modulo relativo:
import logging logging.basicConfig(level=logging.INFO)
Questo configura il logger root per registrare log di messaggi di livello INFO e superiore. I messaggi di registrazione di log che sono meno gravi di questo livello verranno ignorati. I livelli di logging includono DEBUG, INFO, WARNING, ERROR e CRITICAL. Il valore predefinito è WARNING.
I logger in boto3 sono gerarchici. La libreria utilizza alcuni logger diversi, ciascuno corrispondente a diverse parti della libreria. È possibile controllare separatamente il comportamento di ciascuno:
-
boto3: il logger principale per il modulo boto3.
-
botocore: il logger principale per il pacchetto botocore.
-
botocore.auth: per registrare log della creazione di firme AWS per le richieste.
-
botocore.credentials: per registrare log del processo di recupero e aggiornamento delle credenziali.
-
botocore.endpoint: per registrare log della creazione di richieste prima che vengano inviate in rete.
-
botocore.hooks: per registrare log degli eventi attivati nella libreria.
-
botocore.loaders: utilizzato per registrare quando vengono caricate parti di modelli di servizio. AWS
-
botocore.parsers: per registrare log delle risposte dei servizi AWS prima che vengano analizzate.
-
botocore.retryhandler: utilizzato per registrare l'elaborazione dei tentativi di richiesta di servizio (modalità precedente). AWS
-
botocore.retries.standard: utilizzato per registrare l'elaborazione dei tentativi di richiesta di servizio (modalità standard o adattiva). AWS
-
botocore.utils: per registrare log di varie attività nella libreria.
-
botocore.waiter: utilizzato per registrare la funzionalità dei camerieri, che effettuano sondaggi su un servizio fino al raggiungimento di un determinato stato. AWS
Anche altre librerie effettuano la registrazione di log. Internamente, boto3 utilizza l’urllib3 di terze parti per la gestione della connessione HTTP. Quando la latenza è importante, puoi controllarne i log per assicurarti che il pool sia ben utilizzato, osservando quando urllib3 stabilisce una nuova connessione o ne chiude una inattiva.
-
urllib3.connectionpool: per registrare log degli eventi di gestione del pool di connessioni.
Il seguente frammento di codice imposta la maggior parte della registrazione di log su INFO con la registrazione di log di livello DEBUG per l’attività degli endpoint e del pool di connessioni:
import logging logging.getLogger('boto3').setLevel(logging.INFO) logging.getLogger('botocore').setLevel(logging.INFO) logging.getLogger('botocore.endpoint').setLevel(logging.DEBUG) logging.getLogger('urllib3.connectionpool').setLevel(logging.DEBUG)
Hook degli eventi
Botocore emette eventi durante varie fasi della sua esecuzione. È possibile registrare i gestori per questi eventi in modo che ogni volta che viene emesso un evento venga richiamato il gestore. Ciò consente di estendere il comportamento di botocore senza dover modificare i componenti interni.
Ad esempio, si supponga di voler tenere traccia di ogni chiamata di un’operazione PutItem su qualsiasi tabella DynamoDB dell’applicazione. È possibile registrarsi all’evento 'provide-client-params.dynamodb.PutItem' per l’intercettazione e la registrazione di log ogni volta che viene invocata un’operazione PutItem sulla Session associata. Ecco un esempio:
import boto3 import botocore import logging def log_put_params(params, **kwargs): if 'TableName' in params and 'Item' in params: logging.info(f"PutItem on table {params['TableName']}: {params['Item']}") logging.basicConfig(level=logging.INFO) session = boto3.Session() event_system = session.events # Register our interest in hooking in when the parameters are provided to PutItem event_system.register('provide-client-params.dynamodb.PutItem', log_put_params) # Now, every time you use this session to put an item in DynamoDB, # it will log the table name and item data. dynamodb = session.resource('dynamodb') table = dynamodb.Table('YourTableName') table.put_item( Item={ 'pk': '123', 'sk': 'cart#123', 'item_data': 'YourItemData', # ... more attributes ... } )
All’interno del gestore, è possibile anche manipolare i parametri a livello di codice per modificare il comportamento:
params['TableName'] = "NewTableName"
Per ulteriori informazioni sugli eventi, consulta la documentazione botocore sugli eventi
Impaginazione e impaginatore
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, se si desiderano gli ultimi 10 elementi, è possibile utilizzare limit per recuperare solo questi ultimi. Il limite è la quantità che deve essere letta dalla tabella prima di applicare qualsiasi filtro. Non c’è modo di specificare che si desiderano esattamente 10 elementi dopo il filtraggio; è possibile controllare il conteggio prefiltrato e controllare lato client solo quando sono stati effettivamente recuperati 10 elementi. Indipendentemente dal limite, ogni risposta ha sempre una dimensione massima di 1 MB.
Se la risposta include una LastEvaluatedKey, ciò indica che la risposta è terminata perché ha raggiunto un limite di conteggio o dimensione. La chiave è l’ultima chiave valutata per la risposta. È possibile recuperare questa LastEvaluatedKey e passarla a una chiamata successiva come ExclusiveStartKey in modo da leggere il blocco successivo da quel punto di partenza. Quando non viene restituita nessuna LastEvaluatedKey, significa che non ci sono più elementi corrispondenti a Query o Scan.
Ecco un semplice esempio (utilizzando l’interfaccia Resource, ma l’interfaccia Client ha lo stesso schema) che legge al massimo 100 elementi per pagina e si ripete fino a quando tutti gli elementi non sono stati letti.
import boto3 from boto3.dynamodb.conditions import Key dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') query_params = { 'KeyConditionExpression': Key('pk').eq('123') & Key('sk').gt(1000), 'Limit': 100 } while True: response = table.query(**query_params) # Process the items however you like for item in response['Items']: print(item) # No LastEvaluatedKey means no more items to retrieve if 'LastEvaluatedKey' not in response: break # If there are possibly more items, update the start key for the next page query_params['ExclusiveStartKey'] = response['LastEvaluatedKey']
Per comodità, boto3 può occuparsi di questa operazione attraverso gli Impaginatori. Tuttavia, l’opzione funziona solo con l’interfaccia Client. Ecco il codice riscritto per usare gli Impaginatori:
import boto3 dynamodb = boto3.client('dynamodb') paginator = dynamodb.get_paginator('query') query_params = { 'TableName': 'YourTableName', 'KeyConditionExpression': 'pk = :pk_val AND sk > :sk_val', 'ExpressionAttributeValues': { ':pk_val': {'S': '123'}, ':sk_val': {'N': '1000'}, }, 'Limit': 100 } page_iterator = paginator.paginate(**query_params) for page in page_iterator: # Process the items however you like for item in page['Items']: print(item)
Per ulteriori informazioni, consulta la Guida sui paginatori e il riferimento API per.
Nota
Gli Impaginatori hanno anche le proprie impostazioni di configurazione denominate MaxItems, StartingToken e PageSize. Per l’impaginazione con DynamoDB, è necessario ignorare queste impostazioni.
Waiter
I waiter offrono la possibilità di attendere che un’operazione venga completata prima di procedere. Al momento, supportano solo l’attesa della creazione o dell’eliminazione di una tabella. In background, il waiter effettua un controllo ogni 20 secondi fino a 25 volte. È possibile fare questa operazione in autonomia, ma usare un waiter rende elegante il codice di automazione.
Questo codice mostra come attendere la creazione di una particolare tabella:
# Create a table, wait until it exists, and print its ARN response = client.create_table(...) waiter = client.get_waiter('table_exists') waiter.wait(TableName='YourTableName') print('Table created:', response['TableDescription']['TableArn'])
Per ulteriori informazioni, consulta la Guida ai waiter