So implementieren Sie verteilte Sperren in Go in Kombination mit Redis
Szenario für eine einzelne Redis-Instanz
Wenn Sie mit Redis-Befehlen vertraut sind, denken Sie möglicherweise sofort daran, die Set-If-Not-Existenz-Operation von Redis zu verwenden, um sie zu implementieren. Die aktuelle Standardimplementierungsmethode ist SET resources_name my_random_value NX PX 30000 Befehlsreihen, wobei:
Ressourcenname die zu sperrende Ressource bedeutet
NX bedeutet, sie festzulegen, wenn sie nicht vorhanden ist
-
PX 30000 bedeutet, dass die Ablaufzeit 30000 Millisekunden beträgt, also 30 Sekunden
my_random_value Dieser Wert wird von allen Kunden verwendet. Das Ende muss eindeutig sein und alle Erwerber (Konkurrenten) desselben Schlüssels können nicht denselben Wert haben.
Der Wert von value muss eine Zufallszahl sein, hauptsächlich um die Sperre sicherer aufzuheben. Verwenden Sie beim Aufheben der Sperre ein Skript, um Redis Folgendes mitzuteilen: Nur wenn der Schlüssel vorhanden ist und der gespeicherte Wert mit dem von mir angegebenen Wert übereinstimmt Kann mir mitgeteilt werden, dass die Löschung erfolgreich war? Dies kann durch das folgende Lua-Skript erreicht werden:
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
Beispiel: Client A erhält eine Ressourcensperre, wird aber sofort durch einen anderen Vorgang blockiert. Wenn Client A die Sperre nach der Ausführung anderer Vorgänge aufheben möchte, ist die ursprüngliche Sperre bereits festgelegt Und es wurde automatisch von Redis freigegeben, und während dieser Zeit wurde die Ressourcensperre erneut von Client B erworben.
Lua-Skript wird verwendet, da Beurteilung und Löschung zwei Vorgänge sind. Daher ist es möglich, dass A die Sperre automatisch aufhebt, nachdem sie abgelaufen ist, sobald sie beurteilt hat, und B dann die Sperre erworben hat und A dann Del aufruft. Dadurch wird die Sperre von B aufgehoben.
Entsperrungsbeispiel hinzufügen
package main import ( "context" "errors" "fmt" "github.com/brianvoe/gofakeit/v6" "github.com/go-redis/redis/v8" "sync" "time" ) var client *redis.Client const unlockScript = ` if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end` func lottery(ctx context.Context) error { // 加锁 myRandomValue := gofakeit.UUID() resourceName := "resource_name" ok, err := client.SetNX(ctx, resourceName, myRandomValue, time.Second*30).Result() if err != nil { return err } if !ok { return errors.New("系统繁忙,请重试") } // 解锁 defer func() { script := redis.NewScript(unlockScript) script.Run(ctx, client, []string{resourceName}, myRandomValue) }() // 业务处理 time.Sleep(time.Second) return nil } func main() { client = redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", }) var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() ctx, _ := context.WithTimeout(context.Background(), time.Second*3) err := lottery(ctx) if err != nil { fmt.Println(err) } }() go func() { defer wg.Done() ctx, _ := context.WithTimeout(context.Background(), time.Second*3) err := lottery(ctx) if err != nil { fmt.Println(err) } }() wg.Wait() }
Sehen wir uns zunächst die Funktion lottery() an. Bei der Eingabe der Funktion wird zunächst SET resources_name my_random_value NX PX 30000 zum Sperren verwendet Wenn der Vorgang fehlschlägt, kehren Sie direkt zurück und lassen Sie den Benutzer es erneut versuchen. Wenn die Entsperrlogik erfolgreich ausgeführt wird, besteht die Entsperrlogik darin, das oben erwähnte Lua-Skript auszuführen und dann die Geschäftsverarbeitung durchzuführen.
Wir haben zwei Goroutinen in der Funktion main() ausgeführt, um gleichzeitig die Funktion lottery() aufzurufen. Eine der Operationen schlägt direkt fehl, da die Sperre nicht erhalten werden kann.
Zusammenfassung
Zufälligen Wert generieren
Verwenden Sie SET resources_name my_random_value NX PX 30000 zum Sperren
Wenn die Sperre fehlschlägt, kehren Sie direkt zurück
Verzögern Sie, um die Entsperrlogik hinzuzufügen um sicherzustellen, dass es entsperrt wird Wenn die Funktion beendet wird, führen Sie die Geschäftslogik aus Redis-Instanzen sind auf verschiedenen Rechnern verteilt und die meisten Knoten können erfolgreich gesperrt werden. Dies ist der RedLock-Algorithmus. Wir müssen Sperren für mehrere Redis-Instanzen gleichzeitig erwerben, aber dies basiert tatsächlich auf einem einzelnen Instanzalgorithmus.
Entsperrungsbeispiel hinzugefügtpackage main import ( "context" "errors" "fmt" "github.com/brianvoe/gofakeit/v6" "github.com/go-redis/redis/v8" "sync" "time" ) var clients []*redis.Client const unlockScript = ` if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end` func lottery(ctx context.Context) error { // 加锁 myRandomValue := gofakeit.UUID() resourceName := "resource_name" var wg sync.WaitGroup wg.Add(len(clients)) // 这里主要是确保不要加锁太久,这样会导致业务处理的时间变少 lockCtx, _ := context.WithTimeout(ctx, time.Millisecond*5) // 成功获得锁的Redis实例的客户端 successClients := make(chan *redis.Client, len(clients)) for _, client := range clients { go func(client *redis.Client) { defer wg.Done() ok, err := client.SetNX(lockCtx, resourceName, myRandomValue, time.Second*30).Result() if err != nil { return } if !ok { return } successClients <- client }(client) } wg.Wait() // 等待所有获取锁操作完成 close(successClients) // 解锁,不管加锁是否成功,最后都要把已经获得的锁给释放掉 defer func() { script := redis.NewScript(unlockScript) for client := range successClients { go func(client *redis.Client) { script.Run(ctx, client, []string{resourceName}, myRandomValue) }(client) } }() // 如果成功加锁得客户端少于客户端数量的一半+1,表示加锁失败 if len(successClients) < len(clients)/2+1 { return errors.New("系统繁忙,请重试") } // 业务处理 time.Sleep(time.Second) return nil } func main() { clients = append(clients, redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", DB: 0, }), redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", DB: 1, }), redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", DB: 2, }), redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", DB: 3, }), redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", DB: 4, })) var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() ctx, _ := context.WithTimeout(context.Background(), time.Second*3) err := lottery(ctx) if err != nil { fmt.Println(err) } }() go func() { defer wg.Done() ctx, _ := context.WithTimeout(context.Background(), time.Second*3) err := lottery(ctx) if err != nil { fmt.Println(err) } }() wg.Wait() time.Sleep(time.Second) }
Nach dem Login kopierenIm obigen Code verwenden wir die Multi-Datenbank von Redis, um mehrere Redis-Masterinstanzen zu simulieren. In der realen Umgebung sollten diese Instanzen vermieden werden gleichzeitige Ausfälle.
In der Sperrlogik führen wir hauptsächlich SET resources_name my_random_value NX PX 30000 auf jeder Redis-Instanz aus, um die Sperre zu erhalten, und stellen dann den Client, der die Sperre erfolgreich erhalten hat, in einen Kanal (die Verwendung von Slice kann hier Probleme mit der Parallelität haben) und verwenden die Synchronisierung .WaitGroup wartet auf das Ende des Sperrenerfassungsvorgangs. Dann fügen Sie „defer“ hinzu, um die Sperrlogik freizugeben. Die Sperrfreigabelogik ist sehr einfach. Geben Sie einfach die erfolgreich erhaltene Sperre frei. Beurteilen Sie abschließend, ob die Anzahl der erfolgreich erworbenen Sperren mehr als die Hälfte beträgt. Wenn mehr als die Hälfte der Sperren nicht erworben wurden, bedeutet dies, dass die Sperrung fehlgeschlagen ist.Wenn die Sperrung erfolgreich ist, erfolgt im nächsten Schritt die Geschäftsabwicklung.
Zusammenfassung
Generieren Sie einen Zufallswert
Sperren
und senden Sie ihn zur Verwendung an jede Redis-Instanz.- Warten Sie, bis alle Sperrenerfassungsvorgänge abgeschlossen sind Es wird entsperrt, wenn die Funktion die Ausführung verlässt. Hier wird es zuerst verzögert und dann beurteilt, da es möglich ist, die Sperre eines Teils der Redis-Instanz zu erhalten. Da es jedoch nicht mehr als die Hälfte beträgt, wird es dennoch als Sperrfehler beurteilt Beurteilen Sie, ob die Sperre von mehr als der Hälfte der Redis-Instanz erhalten wurde. Wenn keine Erklärung vorliegt. Wenn die Sperre fehlschlägt, kehren Sie direkt zu
-
zurück, um die Geschäftslogik auszuführenSET resource_name my_random_value NX PX 30000
Das obige ist der detaillierte Inhalt vonSo implementieren Sie verteilte Sperren in Go in Kombination mit Redis. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

1. Starten Sie das Menü [Start], geben Sie [cmd] ein, klicken Sie mit der rechten Maustaste auf [Eingabeaufforderung] und wählen Sie Als [Administrator] ausführen. 2. Geben Sie nacheinander die folgenden Befehle ein (kopieren und fügen Sie sie sorgfältig ein): SCconfigwuauservstart=auto, drücken Sie die Eingabetaste. SCconfigbitsstart=auto, drücken Sie die Eingabetaste. SCconfigcryptsvcstart=auto, drücken Sie die Eingabetaste. SCconfigtrustedinstallerstart=auto, drücken Sie die Eingabetaste. SCconfigwuauservtype=share, drücken Sie die Eingabetaste. netstopwuauserv, drücken Sie die Eingabetaste für netstopcryptS

In Go können WebSocket-Nachrichten mit dem Paket gorilla/websocket gesendet werden. Konkrete Schritte: Stellen Sie eine WebSocket-Verbindung her. Senden Sie eine Textnachricht: Rufen Sie WriteMessage(websocket.TextMessage,[]byte("message")) auf. Senden Sie eine binäre Nachricht: Rufen Sie WriteMessage(websocket.BinaryMessage,[]byte{1,2,3}) auf.

Speicherlecks können dazu führen, dass der Speicher des Go-Programms kontinuierlich zunimmt, indem: Ressourcen geschlossen werden, die nicht mehr verwendet werden, wie z. B. Dateien, Netzwerkverbindungen und Datenbankverbindungen. Verwenden Sie schwache Referenzen, um Speicherlecks zu verhindern, und zielen Sie auf Objekte für die Garbage Collection ab, wenn sie nicht mehr stark referenziert sind. Bei Verwendung von Go-Coroutine wird der Speicher des Coroutine-Stapels beim Beenden automatisch freigegeben, um Speicherverluste zu vermeiden.

In der PHP-Entwicklung verbessert der Caching-Mechanismus die Leistung, indem er häufig aufgerufene Daten vorübergehend im Speicher oder auf der Festplatte speichert und so die Anzahl der Datenbankzugriffe reduziert. Zu den Cache-Typen gehören hauptsächlich Speicher-, Datei- und Datenbank-Cache. In PHP können Sie integrierte Funktionen oder Bibliotheken von Drittanbietern verwenden, um Caching zu implementieren, wie zum Beispiel Cache_get() und Memcache. Zu den gängigen praktischen Anwendungen gehören das Zwischenspeichern von Datenbankabfrageergebnissen zur Optimierung der Abfrageleistung und das Zwischenspeichern von Seitenausgaben zur Beschleunigung des Renderings. Der Caching-Mechanismus verbessert effektiv die Reaktionsgeschwindigkeit der Website, verbessert das Benutzererlebnis und reduziert die Serverlast.

In Go können Sie reguläre Ausdrücke verwenden, um Zeitstempel abzugleichen: Kompilieren Sie eine Zeichenfolge mit regulären Ausdrücken, z. B. die, die zum Abgleich von ISO8601-Zeitstempeln verwendet wird: ^\d{4}-\d{2}-\d{2}T \d{ 2}:\d{2}:\d{2}(\.\d+)?(Z|[+-][0-9]{2}:[0-9]{2})$ . Verwenden Sie die Funktion regexp.MatchString, um zu überprüfen, ob eine Zeichenfolge mit einem regulären Ausdruck übereinstimmt.

Zuerst müssen Sie die Systemsprache auf die Anzeige in vereinfachtem Chinesisch einstellen und neu starten. Wenn Sie die Anzeigesprache zuvor auf vereinfachtes Chinesisch geändert haben, können Sie diesen Schritt natürlich einfach überspringen. Beginnen Sie als Nächstes mit dem Betrieb der Registrierung regedit.exe, navigieren Sie direkt zu HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlNlsLanguage in der linken Navigationsleiste oder der oberen Adressleiste und ändern Sie dann den InstallLanguage-Schlüsselwert und den Standardschlüsselwert auf 0804 (wenn Sie ihn in Englisch ändern möchten). us, Sie müssen zunächst die Anzeigesprache des Systems auf en-us einstellen, das System neu starten und dann alles auf 0409 ändern) Sie müssen das System an dieser Stelle neu starten.

Go und die Go-Sprache sind unterschiedliche Einheiten mit unterschiedlichen Eigenschaften. Go (auch bekannt als Golang) ist bekannt für seine Parallelität, schnelle Kompilierungsgeschwindigkeit, Speicherverwaltung und plattformübergreifende Vorteile. Zu den Nachteilen der Go-Sprache gehören ein weniger umfangreiches Ökosystem als andere Sprachen, eine strengere Syntax und das Fehlen dynamischer Typisierung.

Das Verfassen einer klaren und umfassenden Dokumentation ist für das Golang-Framework von entscheidender Bedeutung. Zu den Best Practices gehört die Befolgung eines etablierten Dokumentationsstils, beispielsweise des Go Coding Style Guide von Google. Verwenden Sie eine klare Organisationsstruktur, einschließlich Überschriften, Unterüberschriften und Listen, und sorgen Sie für eine Navigation. Bietet umfassende und genaue Informationen, einschließlich Leitfäden für den Einstieg, API-Referenzen und Konzepte. Verwenden Sie Codebeispiele, um Konzepte und Verwendung zu veranschaulichen. Halten Sie die Dokumentation auf dem neuesten Stand, verfolgen Sie Änderungen und dokumentieren Sie neue Funktionen. Stellen Sie Support und Community-Ressourcen wie GitHub-Probleme und Foren bereit. Erstellen Sie praktische Beispiele, beispielsweise eine API-Dokumentation.
