Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Verrouillage distribué avec le client DynamoDB Lock
Pour les applications qui nécessitent une sémantique traditionnelle de verrouillage, d'acquisition et de libération, DynamoDB Lock Client est une bibliothèque open source qui implémente le verrouillage distribué en utilisant une table DynamoDB comme magasin de verrouillage. Cette approche est utile lorsque vous devez coordonner l'accès à une ressource externe (telle qu'un objet S3 ou une configuration partagée) entre plusieurs instances d'application.
Le client de verrouillage est disponible sous forme de bibliothèque https://github.com/awslabs/amazon-dynamodb-lock-client
Comment ça marche
Le client de verrouillage utilise une table DynamoDB dédiée pour suivre les verrous. Chaque verrou est représenté sous la forme d'un élément doté des attributs clés suivants :
Une clé de partition qui identifie la ressource verrouillée.
Durée du bail qui spécifie la durée de validité du verrou. Si le porte-cadenas tombe en panne ou ne répond plus, le verrou expire automatiquement après la durée du bail.
Un battement de cœur que le détenteur du cadenas envoie périodiquement pour prolonger le bail. Cela empêche le verrou d'expirer alors que le support est toujours en cours de traitement.
Le client de verrouillage utilise des écritures conditionnelles pour s'assurer qu'un seul processus peut acquérir un verrou à la fois. Si un verrou est déjà bloqué, l'appelant peut choisir d'attendre et de réessayer ou d'échouer immédiatement.
Quand utiliser le client de verrouillage
Le client de verrouillage convient parfaitement lorsque :
Vous devez coordonner l'accès à une ressource partagée entre plusieurs instances d'applications ou microservices.
La section critique est longue (quelques secondes à quelques minutes) et il serait coûteux de recommencer l'ensemble de l'opération en cas de conflit.
Vous avez besoin d'une expiration automatique du verrouillage pour gérer correctement les échecs de processus.
Les exemples courants incluent l'orchestration de flux de travail distribués, la coordination des tâches cron entre plusieurs instances et la gestion de l'accès aux ressources externes partagées.
Compromis
- Infrastructure supplémentaire
Nécessite une table DynamoDB dédiée à la gestion des verrous, avec une capacité de lecture et d'écriture supplémentaire pour les opérations de verrouillage et les battements cardiaques.
- Dépendance liée à
L'expiration du verrou dépend de l'horodatage. Un décalage horaire important entre les clients peut entraîner des comportements inattendus, en particulier pour les contrats de location de courte durée.
- Risque d'impasse
Si votre application obtient des verrous sur plusieurs ressources, vous devez les obtenir dans un ordre cohérent pour éviter les blocages. La durée du bail fournit un filet de sécurité en libérant automatiquement les verrous des porteurs qui ne répondent pas.
Mise en œuvre
L'exemple suivant montre comment utiliser le client de verrouillage DynamoDB pour acquérir et débloquer un verrou :
import java.io.IOException; import java.util.Optional; import java.util.concurrent.TimeUnit; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; final DynamoDbClient dynamoDB = DynamoDbClient.builder() .region(Region.US_WEST_2) .build(); final AmazonDynamoDBLockClient lockClient = new AmazonDynamoDBLockClient( AmazonDynamoDBLockClientOptions.builder(dynamoDB, "Locks") .withTimeUnit(TimeUnit.SECONDS) .withLeaseDuration(10L) .withHeartbeatPeriod(3L) .withCreateHeartbeatBackgroundThread(true) .build()); // Try to acquire a lock on a resource final Optional<LockItem> lock = lockClient.tryAcquireLock(AcquireLockOptions.builder("my-shared-resource").build()); if (lock.isPresent()) { try { // Perform operations that require exclusive access processSharedResource(); } finally { // Always release the lock when done lockClient.releaseLock(lock.get()); } } else { System.out.println("Failed to acquire lock."); } lockClient.close();
Important
Relâchez toujours les verrous d'un finally bloc pour vous assurer que les verrous sont libérés même si votre logique de traitement génère une exception. Les verrous non libérés bloquent les autres processus jusqu'à l'expiration du bail.
Vous pouvez également implémenter un mécanisme de verrouillage simple sans la bibliothèque cliente de verrouillage en utilisant directement des écritures conditionnelles. L'exemple suivant utilise UpdateItem une expression de condition pour acquérir un verrou et DeleteItem le libérer :
from datetime import datetime, timedelta from boto3.dynamodb.conditions import Attr def acquire_lock(table, resource_name, owner_id, ttl_seconds): """Attempt to acquire a lock. Returns True if successful.""" expiry = (datetime.now() + timedelta(seconds=ttl_seconds)).isoformat() now = datetime.now().isoformat() try: table.update_item( Key={'LockID': resource_name}, UpdateExpression='SET #owner = :owner, #expiry = :expiry', ConditionExpression=Attr('LockID').not_exists() | Attr('ExpiresAt').lt(now), ExpressionAttributeNames={'#owner': 'OwnerID', '#expiry': 'ExpiresAt'}, ExpressionAttributeValues={':owner': owner_id, ':expiry': expiry} ) return True except table.meta.client.exceptions.ConditionalCheckFailedException: return False def release_lock(table, resource_name, owner_id): """Release a lock. Only succeeds if the caller is the lock owner.""" try: table.delete_item( Key={'LockID': resource_name}, ConditionExpression=Attr('OwnerID').eq(owner_id) ) return True except table.meta.client.exceptions.ConditionalCheckFailedException: return False
Cette approche utilise une expression de condition pour s'assurer qu'un verrou ne peut être acquis que s'il n'existe pas ou a expiré, et qu'il ne peut être libéré que par le processus qui l'a acquis. Envisagez d'activer Time to Live (TTL) sur la table de verrouillage pour nettoyer automatiquement les éléments de verrouillage expirés.