View a markdown version of this page

Verteiltes Sperren mit dem DynamoDB Lock Client - Amazon DynamoDB

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 eine herkömmliche 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 verfügbar.

Funktionsweise

Der Lock-Client verwendet eine dedizierte DynamoDB-Tabelle, um Sperren zu verfolgen. Jede Sperre wird als 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 Herzschlag, den der Schlossinhaber in regelmäßigen Abständen sendet, um den Mietvertrag zu verlängern. Dadurch wird verhindert, dass das Schloss 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 Anrufer wählen, ob er warten und es erneut versuchen möchte oder sofort fehlschlägt.

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 es wäre kostspielig, den gesamten Vorgang im Konfliktfall erneut auszuführen.

  • Sie benötigen ein automatisches Ablaufen der Sperre, 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 Sperrvorgänge und Heartbeats.

Abhängigkeit von der Uhrzeit

Das Ablaufen der Sperre hängt von Zeitstempeln ab. Ein erheblicher Zeitunterschied 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 einrichten, 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

Lösen Sie immer Sperren in einem finally Block, um sicherzustellen, dass Sperren auch dann aufgehoben werden, wenn Ihre Verarbeitungslogik eine Ausnahme auslöst. Ungelöste Sperren blockieren andere Prozesse, bis das Leasing abläuft.

Sie können einen einfachen Sperrmechanismus auch ohne die Lock-Client-Bibliothek implementieren, indem Sie bedingte Schreibvorgänge direkt verwenden. Im folgenden Beispiel wird ein Bedingungsausdruck verwendetUpdateItem, um eine Sperre DeleteItem zu erwerben und sie aufzuheben:

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

Bei diesem Ansatz wird mithilfe eines Bedingungsausdrucks sichergestellt, dass eine Sperre nur erworben werden kann, wenn sie nicht existiert oder abgelaufen ist, und dass sie nur von dem Prozess aufgehoben werden kann, der sie erworben hat. Erwägen Sie, Time to Live (TTL) für die Sperrtabelle zu aktivieren, um abgelaufene Sperrelemente automatisch zu bereinigen.