Verteilte Systeme gibt es schon seit geraumer Zeit, und es gibt bereits etablierte Muster, wenn es um ihr Design geht. Heute werden wir eines der beliebten Muster besprechen: „Sperren“.
Einfach ausgedrückt sind Sperren der Weg, wie Prozesse exklusiven Zugriff auf eine Ressource erhalten, um eine bestimmte Aktion auszuführen. Angenommen, es gibt eine Reihe von Blobs in einem Speicherkonto, und Sie benötigen eine Instanz Ihres Dienstes, um jeden Blob zu verarbeiten, um eine doppelte Verarbeitung zu vermeiden. Der Weg, dies zu tun, wäre, ein Sperre für den Blob zu erhalten, die Verarbeitung abzuschließen und sie freizugeben. Allerdings ergibt sich ein potenzielles Problem, wenn ein Prozess ausfällt, bevor die Sperre freigegeben wird, entweder weil der Prozess abstürzt oder aufgrund einer Netzwerkpartition, wodurch die Ressource unbegrenzt gesperrt bleibt. Dies kann zu Deadlocks und Ressourcenkonflikten führen.
Um Deadlocks zu verhindern, kann eine Strategie verwendet werden, bei der Timeouts oder auf Leasing basierende Sperren eingesetzt werden.
Timeout-Sperre
- In diesem Fall gibt es einen vordefinierten Timeout, für den der Prozess die Sperre anfordert. Wenn die Sperre nicht vor Ablauf des Timeouts freigegeben wird, stellt das System sicher, dass die Sperre letztendlich freigegeben wird.
Leasing-Sperre
-
Für lease-basierte Sperren wird neben dem Timeout-Mechanismus eine Lease-Erneuerungs-API bereitgestellt. Der Prozess, der die Sperre hält, muss diese API aufrufen, bevor die Lease abläuft, um den exklusiven Zugriff auf die Ressource aufrechtzuerhalten. Wenn der Prozess es versäumt, die Lease rechtzeitig zu erneuern, wird die Sperre automatisch freigegeben, sodass andere Prozesse sie übernehmen können.
Vor- und Nachteile von Timeout- und Lease-basierten Sperren
Pros | Cons | |
---|---|---|
Timeout-basierte Sperre | Einfach zu implementieren | Erfordert sorgfältige Auswahl des Timeouts |
Verhindert dauerhafte Sperren | Wenn die Verarbeitung nicht abgeschlossen ist, gibt es keine Möglichkeit, die Lease zu erneuern | |
Lease-basierte Sperre | Reduziert das Risiko vorzeitiger Sperrabläufe |
Erfordert Mechanismus für Lease-Erneuerung |
Prozess kann die Lease weiterhin anfordern, bis die Arbeit abgeschlossen ist. |
Beide oben genannten Strategien sind ein Weg, um schnell von Prozessausfällen oder Netzwerkpartitionen in verteilten Systemen zu erholen.
Lease-Locks-Strategie mit Azure Storage
Sehen wir uns an, wie man die Lease-Locks-Strategie mit Azure Storage verwendet. Dies umfasst auch die Timeout-Lock-Strategie.
Schritt 1: Importieren Sie das Storage Blob Nuget
„12.23.0“ ist die neueste Version zum Zeitpunkt der Verfassung dieses Artikels. Die neuesten Versionen finden Sie unter Azure Storage Blobs.
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.23.0" />
</ItemGroup>
Schritt 2: Erwerben des Leases
Hier ist der Code zum Erwerb des Leases.
public async Task<string> TryAcquireLeaseAsync(string blobName, TimeSpan durationInSeconds, string leaseId = default)
{
BlobContainerClient blobContainerClient = new BlobContainerClient(new Uri($"https://{storageName}.blob.core.windows.net/processors"), tokenCredential, blobClientOptions);
BlobLeaseClient blobLeaseClient = blobContainerClient.GetBlobClient(blobName).GetBlobLeaseClient(leaseId);
try
{
BlobLease lease = await blobLeaseClient.AcquireAsync(durationInSeconds).ConfigureAwait(false);
return lease.LeaseId;
}
catch (RequestFailedException ex) when (ex.Status == 409)
{
return default;
}
}
- Zunächst erstellen wir einen Blob-Container-Client und rufen den Blob-Client für den spezifischen Blob ab, für den wir ein Lease erwerben möchten.
- Zweitens versucht die Methode „Acquire Async“, das Lease für eine bestimmte Dauer zu erwerben. Wenn der Erwerb erfolgreich war, wird eine Lease-ID zurückgegeben. Wenn nicht, wird ein 409 (Statuscode für Konflikt) ausgelöst.
- Das „Acquire Async“ ist die Schlüsselmethode hier. Der Rest des Codes kann entsprechend Ihren Bedürfnissen angepasst/bearbeitet werden.
Schritt 3: Das Lease erneuern
- „Renew Async“ ist die Methode im Storage .NET SDK, die für die Erneuerung des Leases verwendet wird.
- Wenn die Erneuerung nicht erfolgreich ist, wird eine Ausnahme zusammen mit dem Grund für das Versagen ausgelöst.
public async Task ReleaseLeaseAsync(string blobName, string leaseId)
{
BlobLeaseClient blobLeaseClient = this.blobContainerClient.GetBlobClient(blobName).GetBlobLeaseClient(leaseId);
await blobLeaseClient.RenewAsync().ConfigureAwait(false);
}
Schritt 4: Die Acquire- und Renew-Lease-Methoden orchestrieren
- Zunächst rufen wir „Try Acquire Lease Async“ auf, um die Lease-ID aus Schritt 2 abzurufen. Sobald dies erfolgreich ist, wird eine Hintergrundaufgabe gestartet, die alle X Sekunden „Renew Lease Async“ aus Schritt 3 aufruft. Stellen Sie nur sicher, dass genügend Zeit zwischen dem Timeout und dem Zeitpunkt, an dem die Methode zur Verlängerung des Leasings aufgerufen wird, liegt.
string leaseId = await this.blobReadProcessor.TryAcquireLeaseAsync(blobName, TimeSpan.FromSeconds(60)).ConfigureAwait(false);
Task leaseRenwerTask = this.taskFactory.StartNew(
async () =>
{
while (leaseId != default && !cancellationToken.IsCancellationRequested)
{
await Task.Delay(renewLeaseMillis).ConfigureAwait(false);
await this.blobReadProcessor.RenewLeaseAsync(blobName, leaseId).ConfigureAwait(false);
}
},
CancellationToken.None,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
- Das Abbruch-Token wird verwendet, um die Leasingverlängerungsaufgabe ordnungsgemäß zu stoppen, wenn sie nicht mehr benötigt wird.
Schritt 5: Leasingverlängerung abbrechen
- Wenn die Methode „Cancel Async“ aufgerufen wird, wird „IsCancellationRequested“ in Schritt 4 wahr, weshalb wir die While-Schleife nicht mehr betreten und keine Leasingverlängerung anfordern.
await cancellationTokenSource.CancelAsync().ConfigureAwait(false);
await leaseRenwerTask.WaitAsync(Timeout.InfiniteTimeSpan).ConfigureAwait(false);
Schritt 6: Leasing freigeben
Um das Leasing schließlich freizugeben, rufen Sie einfach die Methode „Release Async“ auf.
public async Task ReleaseLeaseAsync(string blobName, string leaseId)
{
BlobLeaseClient blobLeaseClient = this.blobContainerClient.GetBlobClient(blobName).GetBlobLeaseClient(leaseId);
await blobLeaseClient.ReleaseAsync().ConfigureAwait(false);
}
Fazit
Locks gehören zu den grundlegenden Mustern in verteilten Systemen, um exklusiven Zugriff auf Ressourcen zu erhalten. Es ist notwendig, die Fallstricke im Umgang mit ihnen im Auge zu behalten, um einen reibungslosen Ablauf der Operationen zu gewährleisten. Durch die Verwendung von Azure Storage können wir diese effizienten Lock-Mechanismen implementieren, die unbestimmtes Blockieren verhindern und gleichzeitig Flexibilität in der Handhabung der Locks bieten.
Source:
https://dzone.com/articles/locks-in-distributed-systems-timeout-lease-based