Comment implémenter des verrous distribués dans Go combinés avec Redis
Scénario d'instance Redis unique
Si vous êtes familier avec les commandes Redis, vous pouvez immédiatement penser à utiliser l'opération set if not exist de Redis pour l'implémenter, et la méthode d'implémentation standard actuelle est le SET nom_ressource my_random_value NX PX 30000 séries de commandes, où :
resource_name signifie la ressource à verrouiller
NX signifie la définir si elle n'existe pas
-
PX 30000 signifie que le délai d'expiration est de 30000 millisecondes, soit 30 secondes
my_random_value Cette valeur est utilisée par tous les clients. La fin doit être unique, et tous les acquéreurs (concurrents) d'une même clé ne peuvent pas avoir la même valeur.
La valeur de value doit être un nombre aléatoire principalement pour déverrouiller le verrou de manière plus sûre, utilisez un script pour indiquer à Redis : Uniquement lorsque la clé existe et que la valeur stockée est la même que la valeur que j'ai spécifiée. puis-je être informé que la suppression a réussi. Ceci peut être réalisé grâce au script Lua suivant :
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
Par exemple : le client A obtient un verrou de ressource, mais est immédiatement bloqué par une autre opération. Lorsque le client A souhaite libérer le verrou après avoir exécuté d'autres opérations, le verrou d'origine a déjà été chronométré. out. Et il a été automatiquement libéré par Redis, et pendant cette période, le verrouillage des ressources a été à nouveau acquis par le client B.
Le script Lua est utilisé car le jugement et la suppression sont deux opérations, il est donc possible que A libère automatiquement le verrou après son expiration dès qu'il a jugé, puis B a acquis le verrou, puis A appelle Del, provoquant le déverrouillage de B.
Ajouter un exemple de déverrouillage
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() }
Regardons d'abord la fonction lottery() Ici, lors de la saisie de la fonction, utilisez d'abord SET resource_name my_random_value NX PX 30000 pour verrouiller. valeur. Si l'opération échoue, retournez directement et laissez l'utilisateur réessayer. Si la logique de déverrouillage est exécutée avec succès en différé, la logique de déverrouillage consiste à exécuter le script Lua mentionné ci-dessus, puis à effectuer le traitement métier.
Nous avons exécuté deux goroutines dans la fonction main() pour appeler simultanément la fonction lottery(). L'une des opérations échouera directement car le verrou ne peut pas être obtenu.
Résumé
Générer une valeur aléatoire
Utilisez SET nom_ressource my_random_value NX PX 30000 pour verrouiller
Si le verrouillage échoue, revenez directement
defer pour ajouter une logique de déverrouillage pour vous en assurer sera débloqué lorsque la fonction se termine, Exécuter
Exécuter la logique métier
Scénarios d'instances Redis multiples
Dans le cas d'une seule instance, si cette instance se bloque, toutes les requêtes échoueront car le verrou ne peut pas être obtenu, nous avons donc besoin de plusieurs Instances Redis distribuées dans différents L'instance Redis sur la machine et le verrouillage de la plupart des nœuds peuvent être verrouillés avec succès. Il s'agit de l'algorithme RedLock. Nous devons acquérir des verrous sur plusieurs instances Redis en même temps, mais cela est en fait basé sur un algorithme à instance unique.
Exemple de déverrouillage ajouté
package 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) }
Dans le code ci-dessus, nous utilisons la multi-base de données de Redis pour simuler plusieurs instances maîtres Redis. Généralement, nous choisirons 5 instances Redis. Dans l'environnement réel, ces instances doivent être distribuées sur différentes machines. échecs simultanés.
Dans la logique de verrouillage, nous exécutons principalement SET resource_name my_random_value NX PX 30000 sur chaque instance Redis pour obtenir le verrou, puis plaçons le client qui a réussi à obtenir le verrou dans un canal (il peut y avoir des problèmes de concurrence lors de l'utilisation de slice ici), et utilisez sync.WaitGroup attend la fin de l’opération d’acquisition du verrou.
Ensuite, ajoutez un délai pour libérer la logique de verrouillage. La logique de déverrouillage est très simple, il suffit de libérer le verrou obtenu avec succès.
Enfin, jugez si le nombre de verrous acquis avec succès est supérieur à la moitié. Si plus de la moitié des verrous ne sont pas acquis, cela signifie que le verrouillage a échoué.
Si le verrouillage réussit, l'étape suivante consiste à effectuer le traitement commercial.
Résumé
Générer une valeur aléatoire
et l'envoyer à chaque instance Redis pour utilisation
SET resource_name my_random_value NX PX 30000
LockAttendez que toutes les opérations d'acquisition de verrou soient terminées
-
defer ajoute une logique de déverrouillage pour garantir que il sera déverrouillé à la sortie de la fonction Exécution, ici différer d'abord puis juger car il est possible d'obtenir le verrou d'une partie de l'instance Redis, mais comme il ne dépasse pas la moitié, il sera quand même jugé comme un échec de verrouillage
Déterminez si le verrouillage de plus de la moitié de l'instance Redis a été obtenu, s'il n'y a pas d'explication. Si le verrouillage échoue, revenez directement à
pour exécuter la logique métier
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Le mode Redis Cluster déploie les instances Redis sur plusieurs serveurs grâce à la rupture, à l'amélioration de l'évolutivité et de la disponibilité. Les étapes de construction sont les suivantes: Créez des instances de redis étranges avec différents ports; Créer 3 instances Sentinel, Moniteur Redis Instances et basculement; Configurer les fichiers de configuration Sentinel, ajouter des informations d'instance Redis de surveillance et des paramètres de basculement; Configurer les fichiers de configuration d'instance Redis, activer le mode de cluster et spécifier le chemin du fichier d'informations de cluster; Créer un fichier nœuds.conf, contenant des informations de chaque instance redis; Démarrez le cluster, exécutez la commande CREATE pour créer un cluster et spécifiez le nombre de répliques; Connectez-vous au cluster pour exécuter la commande d'informations de cluster pour vérifier l'état du cluster; faire

Comment effacer les données Redis: utilisez la commande flushall pour effacer toutes les valeurs de clé. Utilisez la commande flushdb pour effacer la valeur clé de la base de données actuellement sélectionnée. Utilisez SELECT pour commuter les bases de données, puis utilisez FlushDB pour effacer plusieurs bases de données. Utilisez la commande del pour supprimer une clé spécifique. Utilisez l'outil Redis-CLI pour effacer les données.

Pour lire une file d'attente à partir de Redis, vous devez obtenir le nom de la file d'attente, lire les éléments à l'aide de la commande LPOP et traiter la file d'attente vide. Les étapes spécifiques sont les suivantes: Obtenez le nom de la file d'attente: Nommez-le avec le préfixe de "Fitre:" tel que "Fitre: My-Quyue". Utilisez la commande LPOP: éjectez l'élément de la tête de la file d'attente et renvoyez sa valeur, telle que la file d'attente LPOP: My-Queue. Traitement des files d'attente vides: si la file d'attente est vide, LPOP renvoie NIL et vous pouvez vérifier si la file d'attente existe avant de lire l'élément.

L'utilisation de la directive Redis nécessite les étapes suivantes: Ouvrez le client Redis. Entrez la commande (Verbe Key Value). Fournit les paramètres requis (varie de l'instruction à l'instruction). Appuyez sur Entrée pour exécuter la commande. Redis renvoie une réponse indiquant le résultat de l'opération (généralement OK ou -err).

L'utilisation des opérations Redis pour verrouiller nécessite l'obtention du verrouillage via la commande setnx, puis en utilisant la commande Expire pour définir le temps d'expiration. Les étapes spécifiques sont les suivantes: (1) Utilisez la commande setnx pour essayer de définir une paire de valeurs de clé; (2) Utilisez la commande Expire pour définir le temps d'expiration du verrou; (3) Utilisez la commande del pour supprimer le verrouillage lorsque le verrouillage n'est plus nécessaire.

La meilleure façon de comprendre le code source redis est d'aller étape par étape: familiarisez-vous avec les bases de Redis. Sélectionnez un module ou une fonction spécifique comme point de départ. Commencez par le point d'entrée du module ou de la fonction et affichez le code ligne par ligne. Affichez le code via la chaîne d'appel de fonction. Familiez les structures de données sous-jacentes utilisées par Redis. Identifiez l'algorithme utilisé par Redis.

Les causes de la perte de données redis incluent les défaillances de mémoire, les pannes de courant, les erreurs humaines et les défaillances matérielles. Les solutions sont: 1. Stockez les données sur le disque avec RDB ou AOF Persistance; 2. Copiez sur plusieurs serveurs pour une haute disponibilité; 3. Ha avec Redis Sentinel ou Redis Cluster; 4. Créez des instantanés pour sauvegarder les données; 5. Mettre en œuvre les meilleures pratiques telles que la persistance, la réplication, les instantanés, la surveillance et les mesures de sécurité.

Utilisez l'outil de ligne de commande redis (Redis-CLI) pour gérer et utiliser Redis via les étapes suivantes: Connectez-vous au serveur, spécifiez l'adresse et le port. Envoyez des commandes au serveur à l'aide du nom et des paramètres de commande. Utilisez la commande d'aide pour afficher les informations d'aide pour une commande spécifique. Utilisez la commande QUIT pour quitter l'outil de ligne de commande.
