Zurück zur Startseite

RedLock.NET: verteilte Sperren in .NET

Der Artikel beschreibt die Implementierung verteilter Sperren in .NET mit RedLock.NET und Alternativen über PostgreSQL. Standardprimitive, Mutex, DB-Sperren werden verglichen. SQL-Beispiele und Best Practices für Mikroservices in Kubernetes.

Verteilte Sperren in .NET: RedLock vs. DB-Ansätze
Advertisement 728x90

Implementierung verteilter Sperren in .NET mit RedLock.NET

Verteilte Sperren ermöglichen exklusiven Zugriff auf kritische Abschnitte in Systemen mit mehreren Service-Instanzen. Dies ist entscheidend für Microservices in Kubernetes, wo Pods oder verschiedene Services um Ressourcen konkurrieren. Das primäre Ziel ist die atomare Ausführung von Operationen, wie das Aktualisieren wichtiger Daten in der Datenbank oder das Aufrufen externer APIs.

Typische Szenarien:

  • Ändern geschäftskritischer Werte in der Datenbank.
  • Senden von Anfragen an Regulierungssysteme (z. B. Äquivalente zu Chestny Znak).
  • Synchronisation zwischen Pods desselben Microservices oder Services in Orchestrierung/Choreographie.

Standard-.NET-Primitiven wie SemaphoreSlim, AsyncLock oder lock sind aufgrund der physischen Trennung der Instanzen nicht geeignet. Mutex funktioniert unter Windows, ist aber in asynchronem Code und Docker-Containern problematisch.

Google AdInline article slot

Überblick über RedLock.NET

RedLock.NET ist eine Bibliothek zur Implementierung des RedLock-Algorithmus in .NET. Die neueste stabile Version ist 2.3.2 (2022). Das Paket hat Sicherheitsprüfungen bestanden, ohne bekannte Schwachstellen. Der Autor pflegt Issues aktiv auf GitHub.

Der RedLock-Algorithmus nutzt einen Redis-Cluster für verteilte Sperren mit TTL und gewährleistet Sicherheit auch bei Netzwerkausfällen. Die Bibliothek kapselt die Logik zum Erstellen, Verlängern und Freigeben von Sperren.

Alternativen: Sperren über Datenbank

Sperrentabelle in PostgreSQL

Der klassische Ansatz ist eine dedizierte Tabelle für Sperren:

Google AdInline article slot
CREATE TABLE locks (
    resource VARCHAR(100) PRIMARY KEY,
    locked_by VARCHAR(100),
    locked_at TIMESTAMPTZ DEFAULT NOW(),
    expires_at TIMESTAMPTZ
);

Atomarer INSERT mit Konfliktbehandlung:

INSERT INTO locks (resource, locked_by, expires_at)
VALUES (@resource, @locked_by, NOW() + (@timespan || ' seconds')::interval)
ON CONFLICT (resource) DO UPDATE
SET locked_by = EXCLUDED.locked_by,
    expires_at = EXCLUDED.expires_at
WHERE locks.expires_at < NOW()
RETURNING locked_by;

Logik:

  • Fehlt die Sperre, wird eine neue mit TTL erstellt.
  • Ist die Sperre abgelaufen, wird sie aktualisiert.
  • Ist sie aktiv, wird ein leeres Ergebnis zurückgegeben.

Anforderungen:

Google AdInline article slot
  • Eindeutige resource pro Operation.
  • Eindeutige locked_by (GUID oder Instanz-ID + Thread-ID).

Isolationsstufen

SERIALIZABLE gewährleistet Atomicität ohne zusätzliche Tabellen, reduziert aber den Durchsatz. In Unternehmensumgebungen genehmigen Datenbankadministratoren es selten wegen der Belastung. UPSERT-Ansätze mit READ COMMITTED sind bevorzugt.

Vergleich der Ansätze

| Ansatz | Vorteile | Nachteile |

|-----------------|-------------------------------|------------------------------------|

| .NET-Primitiven | Einfachheit, niedrige Latenz | Nur für einzelne Instanz |

| Mutex | Synchronisation auf OS-Ebene | Nicht für Docker/Linux, async-Probleme |

| DB-Sperren | Integration in die Datenbank | Overhead der Datenbank, Skalierbarkeitsprobleme |

| RedLock.NET | Hohe Verfügbarkeit, TTL | Abhängigkeit von Redis |

Datenbank-Sperren eignen sich gut für einfache Fälle mit niedriger Frequenz. RedLock.NET ist ideal für Szenarien mit hoher Last und starken Garantien.

Praktische Implementierung von RedLock.NET

Installation: dotnet add package RedLock.net. Erstellen einer RedLockFactory mit Redis-Endpunkten:

var multiplexer = new ConnectionMultiplexer(...);
var factory = RedLockFactory.Create(...);
using var @lock = await factory.CreateLockAsync(resource, expiry);
if (@lock.IsAcquired) {
    // critical section
}

Wichtige Parameter:

  • resource: String-Identifier (Redis-Schlüssel).
  • expiry: TimeSpan für TTL.
  • Quorum: Mehrheit der Redis-Knoten für Konsens.

Fehlerbehandlung: Die Bibliothek verlängert Sperren für lange Operationen automatisch über IExtensibleLock.

Skalierung und Best Practices

  • Redis Sentinel oder Cluster für HA verwenden.
  • Uhrzeitsprung < 10 ms zwischen Knoten.
  • Gültigkeitszeit > 2x Uhrzeitsprung + Operationzeit.
  • Fehlerrate beim Sperrenwerb überwachen.

In der Produktion mit Circuit Breakern für externe Aufrufe in kritischen Abschnitten kombinieren.

Wichtige Erkenntnisse

  • RedLock.NET bietet quorum-basierte Sicherheit ohne Single Point of Failure.
  • Datenbank-Sperren sind einfacher, aber anfällig für Datenbanküberlastung und skalieren nicht.
  • Immer TTL setzen, um gegen Zombie-Sperren zu schützen.
  • Unter Last mit Netzwerkpartition-Simulationen testen.
  • Für .NET Core und neuer Mutex in Containern vermeiden.

— Editorial Team

Advertisement 728x90

Weiterlesen