L'évolution du verrouillage distribué redisTemplate et la mise en œuvre du verrouillage distribué redission !

L'évolution des verrous distribués

Fondamental

Nous pouvons nous rendre à un endroit en même temps pour "occuper une fosse", si nous l'occupons, nous exécuterons la logique. Sinon, vous devez attendre que le verrou soit déverrouillé. « Occuper les fosses » peut accéder à Redis, à la base de données ou à tout autre endroit auquel tout le monde peut accéder. En attendant un moyen de tourner.

Première étape


 

public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
        //阶段一
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111");
        //获取到锁,执行业务
        if (lock) {
            Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
            //删除锁,如果在此之前报错或宕机会造成死锁
            stringRedisTemplate.delete("lock");
            return categoriesDb;
        }else {
            //没获取到锁,等待100ms重试
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return getCatalogJsonDbWithRedisLock();
        }
    }
 
public Map<String, List<Catalog2Vo>> getCategoryMap() {
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        String catalogJson = ops.get("catalogJson");
        if (StringUtils.isEmpty(catalogJson)) {
            System.out.println("缓存不命中,准备查询数据库。。。");
            Map<String, List<Catalog2Vo>> categoriesDb= getCategoriesDb();
            String toJSONString = JSON.toJSONString(categoriesDb);
            ops.set("catalogJson", toJSONString);
            return categoriesDb;
        }
        System.out.println("缓存命中。。。。");
        Map<String, List<Catalog2Vo>> listMap = JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Catalog2Vo>>>() {});
        return listMap;
    }

Problèmes : 1. Setnx a occupé la position, mais le code métier est anormal ou le programme plante pendant le processus de page. La logique de verrouillage de suppression n'est pas exécutée, ce qui provoque un blocage.

Solution : définissez l'expiration automatique du verrouillage. Même s'il n'est pas supprimé, il sera automatiquement supprimé.

Étape 2


 

public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111");
        if (lock) {
            //设置过期时间
            stringRedisTemplate.expire("lock", 30, TimeUnit.SECONDS);
            Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
            stringRedisTemplate.delete("lock");
            return categoriesDb;
        }else {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return getCatalogJsonDbWithRedisLock();
        }​​​​​​​

Problèmes : 1. Setnx est configuré et lorsque je suis sur le point de définir le délai d'expiration, il plante. Encore une fois dans l’impasse. Solution : La définition du délai d'expiration et des espaces réservés doit être atomique. redis prend en charge l'utilisation de la commande setnx ex

public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
    //加锁的同时设置过期时间,二者是原子性操作
    Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "1111",5, TimeUnit.SECONDS);
    if (lock) {
        Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
        //模拟超长的业务执行时间
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        stringRedisTemplate.delete("lock");
        return categoriesDb;
    }else {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return getCatalogJsonDbWithRedisLock();
    }
}

Question : 1. Supprimer le verrou directement ? ? ? Si le verrou lui-même expire en raison d'une longue période d'activité et que nous le supprimons directement, nous pouvons supprimer le verrou détenu par d'autres. Solution : lorsque vous occupez un verrou, spécifiez la valeur comme uuid, et tout le monde ne la supprimera que si elle correspond à son propre verrou.

Quatrième étape

public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
        String uuid = UUID.randomUUID().toString();
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
         //为当前锁设置唯一的uuid,只有当uuid相同时才会进行删除锁的操作
        Boolean lock = ops.setIfAbsent("lock", uuid,5, TimeUnit.SECONDS);
        if (lock) {
            Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
            String lockValue = ops.get("lock");
            if (lockValue.equals(uuid)) {
                try {
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                stringRedisTemplate.delete("lock");
            }
            return categoriesDb;
        }else {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return getCatalogJsonDbWithRedisLock();
        }
    }


Questions : 1. S'il s'agit de la valeur actuelle, lorsque vous êtes sur le point de supprimer le verrou, celui-ci a expiré et quelqu'un d'autre l'a défini sur une nouvelle valeur. Ensuite, ce que nous supprimons, c'est le verrou de quelqu'un d'autre. Solution : la suppression du verrou doit garantir l'atomicité. Terminé à l'aide du script redis+Lua

Cinquième étape – Forme finale

 public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
        String uuid = UUID.randomUUID().toString();
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        Boolean lock = ops.setIfAbsent("lock", uuid,5, TimeUnit.SECONDS);
        if (lock) {
            Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
            String lockValue = ops.get("lock");
            String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
                    "    return redis.call(\"del\",KEYS[1])\n" +
                    "else\n" +
                    "    return 0\n" +
                    "end";
            stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), lockValue);
            return categoriesDb;
        }else {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return getCatalogJsonDbWithRedisLock();
        }
    }

4) Redisson
Redisson est une grille de données Java en mémoire (In-Memory Data Grid) implémentée sur la base de Redis. Il fournit non seulement une série d'objets Java communs distribués, mais fournit également de nombreux services distribués. Ceux-ci incluent (BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service. , Service de planification) Redisson fournit le moyen le plus simple et le plus pratique d'utiliser Redis. Le but de Redisson est de promouvoir la séparation des préoccupations des utilisateurs (Separation of Concern) de Redis, afin que les utilisateurs puissent se concentrer davantage sur le traitement de la logique métier.

 

 

Je suppose que tu aimes

Origine blog.csdn.net/guoweiting/article/details/115379589
conseillé
Classement