Les verrous distribués peuvent être implémentés de plusieurs manières, telles que zookeeper, redis... Quoi qu'il en soit, le principe de base reste le même : une valeur d'état est utilisée pour représenter la serrure, et l'occupation et la libération de la serrure sont identifiées par la valeur d'état.
1. Pourquoi Redis peut facilement implémenter des verrous distribués
1. Redis est un mode mono-processus, utilisant un seul thread. files d'attente Le mode transforme l'accès simultané en accès série et il n'y a pas de concurrence entre les connexions de plusieurs clients à Redis.
2. La commande SETNX de Redis peut facilement implémenter des verrous distribués.
setNX (SET if Not eXists)
Syntaxe : valeur de la clé SETNX
Valeur de retour : si le paramètre est réussi, 1 est renvoyé si le paramètre échoue, 0 est ; est revenu.
Définissez la valeur de key sur value si et seulement si la key n'existe pas, et renvoyez 1 si la clé donnée existe déjà, SETNX ne fait rien et renvoie 0.
Pour résumer, vous pouvez utiliser la valeur de retour de setnx pour déterminer si le verrou est acquis, et vous n'avez pas à vous soucier de l'accès simultané, car Redis est monothread, donc s'il renvoie 1 , le verrou est acquis et 0 est renvoyé. Il n'a pas été obtenu. Lorsque l'opération commerciale est terminée, le verrou doit être libéré. La logique de libération du verrou est très simple, qui consiste à supprimer la clé précédemment définie, afin que le verrou puisse être obtenu en définissant la clé la prochaine fois.
2. Implémentation du verrou distribué
Nous savons déjà que les verrous distribués peuvent être implémentés via la fonction setNX fournie avec Redis. Les étapes de mise en œuvre spécifiques sont les suivantes.
J'ai installé le service Redis sur une machine virtuelle Linux CentOS7. L'adresse IP est : 192.168.246.130 et le port du service est : 6379.
Ce qui suit est un exemple de Java implémentant des verrous distribués via Redis :
import redis.clients.jedis.Jedis; public class RedisLock { //锁的key private static final String key = "DistributedRedisLock"; private static Integer count = 0; public static void main(String[] args) { for(int i=0;i<1000;i++){ new Thread(new Runnable() { @Override public void run() { //获取Redis连接 Jedis jedis = new Jedis("192.168.246.130", 6379); try{ while(true){ //获取锁 if(jedis.setnx(key, Thread.currentThread().getName()) == 1){ try{ System.out.println("线程("+Thread.currentThread().getName()+")获取到锁,开始执行操作"); count++; System.out.println(count); break; }finally{ System.out.println("操作执行完成,释放锁"); //操作执行完一定要释放锁,所以在finally块中执行 jedis.del(key); } }else{ //返回的不是1,说明已经有某个线程获取到了锁 try { //等待100毫秒之后重试 Thread.sleep(100l); } catch (InterruptedException e) { e.printStackTrace(); } } } }catch(Exception e){ e.printStackTrace(); }finally{ //释放Redis连接 jedis.disconnect(); } } }).start(); } } }
La sortie du code ci-dessus est :
线程(Thread-320)获取到锁,开始执行操作 1 操作执行完成,释放锁 线程(Thread-463)获取到锁,开始执行操作 2 操作执行完成,释放锁 线程(Thread-997)获取到锁,开始执行操作 3 操作执行完成,释放锁 ... 线程(Thread-409)获取到锁,开始执行操作 998 操作执行完成,释放锁 线程(Thread-742)获取到锁,开始执行操作 999 操作执行完成,释放锁 线程(Thread-286)获取到锁,开始执行操作 1000 操作执行完成,释放锁
Bien que le code ci-dessus soit dans une seule application avec plusieurs threads Il a été testé, mais même si plusieurs applications et plusieurs threads sont utilisés pour acquérir des verrous dans un environnement distribué, les résultats sont toujours corrects.
3. Résoudre le problème de blocage
L'exemple de code précédent est juste un code de test, juste pour illustrer le principe. L'exemple lui-même est très simple, il y en a donc. certains aspects inconsidérés. Par exemple, après l'acquisition du verrou, un problème environnemental se produit lors de l'exécution de l'opération commerciale et la connexion à Redis est déconnectée. Le verrou ne peut alors pas être libéré dans le bloc final, ce qui oblige les autres threads en attente d'acquérir le verrou à attendre indéfiniment. c’est ce qui se produit.
Solution :
Vous pouvez définir un délai d'expiration pour le verrou dans Redis, de sorte que même si le verrou ne peut pas être libéré, le verrou peut être libéré automatiquement après un certain temps. période de temps.
En termes de code, il vous suffit d'ajouter le code suivant au bloc d'instruction try après avoir acquis le verrou :
jedis.expire(key, 10); //这里给锁设置10秒的过期时间
Une meilleure solution :
Non. Une solution n'est pas très bonne, car lorsque le temps de traitement de l'opération commerciale est très long et dépasse le délai d'expiration défini, le verrou est automatiquement libéré, puis lorsque l'opération de libération du verrou dans le bloc final est exécutée, le verrou peut avoir été utilisé par d'autres utilisateurs. S'il est détenu par un thread, les verrous détenus par d'autres threads seront libérés, provoquant des problèmes de concurrence. Par conséquent, une manière plus appropriée consiste à déterminer si le verrou a expiré lors de la libération du verrou. S'il a expiré, il n'est pas nécessaire de le libérer à nouveau.
Dans le code, changez l'opération après acquisition de la serrure par le code suivant :
long start = System.currentTimeMillis(); //获取起始时间毫秒数 try{ jedis.expire(key, 10); ... }finally{ ... if(System.currentTimeMillis() < start+10*1000){ //如果之前设置的锁还未过期,则释放掉 jedis.del(key); } }
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!