Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.
Verteiltes Sperren mit dem DynamoDB Lock Client
Für Anwendungen, die traditionelle lock-acquire-release Semantik erfordern, ist der DynamoDB Lock Client eine Open-Source-Bibliothek, die verteiltes Sperren mithilfe einer DynamoDB-Tabelle als Sperrspeicher implementiert. Dieser Ansatz ist nützlich, wenn Sie den Zugriff auf eine externe Ressource (z. B. ein S3-Objekt oder eine gemeinsam genutzte Konfiguration) über mehrere Anwendungsinstanzen hinweg koordinieren müssen.
Der Lock-Client ist als Open-Source-Java-Bibliothek
Funktionsweise
Der Lock-Client verwendet eine dedizierte DynamoDB-Tabelle, um Sperren zu verfolgen. Jede Sperre wird als ein Element mit den folgenden Schlüsselattributen dargestellt:
Ein Partitionsschlüssel, der die gesperrte Ressource identifiziert.
Eine Leasingdauer, die angibt, wie lange die Sperre gültig ist. Wenn der Schlosshalter abstürzt oder nicht mehr reagiert, läuft die Sperre nach Ablauf der Leasingdauer automatisch ab.
Ein Taktsignal, das der Schlossinhaber regelmäßig sendet, um den Leasingvertrag zu verlängern. Dadurch wird verhindert, dass die Sperre abläuft, während der Inhaber die Bearbeitung noch aktiv durchführt.
Der Lock-Client verwendet bedingte Schreibvorgänge, um sicherzustellen, dass jeweils nur ein Prozess eine Sperre erhalten kann. Wenn eine Sperre bereits aktiviert ist, kann der Aufrufer wählen, ob er warten und es erneut versuchen möchte oder ob er sofort fehlschlagen möchte.
Wann sollte der Lock-Client verwendet werden
Der Lock-Client eignet sich gut, wenn:
Sie müssen den Zugriff auf eine gemeinsam genutzte Ressource über mehrere Anwendungsinstanzen oder Microservices hinweg koordinieren.
Der kritische Abschnitt dauert lange (Sekunden bis Minuten), und im Konfliktfall wäre ein erneuter Versuch, den gesamten Vorgang zu wiederholen, teuer.
Sie benötigen ein automatisches Ablaufen der Sperren, um Prozessfehler ordnungsgemäß behandeln zu können.
Zu den häufigsten Beispielen gehören die Orchestrierung verteilter Workflows, die Koordination von Cron-Jobs über mehrere Instanzen hinweg und die Verwaltung des Zugriffs auf gemeinsam genutzte externe Ressourcen.
Kompromisse
- Zusätzliche Infrastruktur
Erfordert eine dedizierte DynamoDB-Tabelle für die Sperrverwaltung mit zusätzlicher Lese- und Schreibkapazität für Sperroperationen und Heartbeats.
- Abhängigkeit von der Uhr
Das Ablaufen von Sperren hängt von Zeitstempeln ab. Ein erheblicher Zeitversatz zwischen den Clients kann zu unerwartetem Verhalten führen, insbesondere bei kurzen Leasingdauern.
- Deadlock-Risiko
Wenn Ihre Anwendung Sperren für mehrere Ressourcen erwirbt, müssen Sie diese in einer konsistenten Reihenfolge aktivieren, um Deadlocks zu vermeiden. Die Leasingdauer bietet ein Sicherheitsnetz, da Sperren von Inhabern, die nicht darauf reagieren, automatisch aufgehoben werden.
Implementierung
Das folgende Beispiel zeigt, wie der DynamoDB Lock Client verwendet wird, um eine Sperre zu erwerben und aufzuheben:
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();
Wichtig
Geben Sie immer Sperren in einem finally Block auf, um sicherzustellen, dass Sperren auch dann aufgehoben werden, wenn Ihre Verarbeitungslogik eine Ausnahme auslöst. Nicht freigegebene Sperren blockieren andere Prozesse, bis das Leasing abläuft.
Sie können auch einen einfachen Sperrmechanismus ohne die Lock-Client-Bibliothek implementieren, indem Sie bedingte Schreibvorgänge direkt verwenden. Im folgenden Beispiel wird UpdateItem mit einem Bedingungsausdruck eine Sperre DeleteItem abgerufen und aufgehoben:
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
Dieser Ansatz verwendet einen Bedingungsausdruck, um sicherzustellen, dass eine Sperre nur dann erworben werden kann, wenn sie nicht existiert oder abgelaufen ist, und dass sie nur von dem Prozess freigegeben werden kann, der sie erworben hat. Erwägen Sie, Time to Live (TTL) in der Sperrtabelle zu aktivieren, um abgelaufene Sperrobjekte automatisch zu bereinigen.