¡La evolución del bloqueo distribuido redisTemplate y la implementación del bloqueo distribuido redission!

La evolución de las cerraduras distribuidas.

Fundamental

Podemos ir a un lugar al mismo tiempo a "ocupar un hoyo", si lo ocupamos ejecutaremos la lógica. De lo contrario deberá esperar hasta que se libere el bloqueo. Los "pozos ocupados" pueden ir a Redis, la base de datos o cualquier lugar al que todos puedan acceder. Esperando una manera de girar.

La etapa uno


 

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;
    }

Problemas: 1. Setnx ha ocupado la posición, pero el código comercial es anormal o el programa falla durante el proceso de la página. La lógica de bloqueo de eliminación no se ejecuta, lo que provoca un punto muerto.

Solución: configure la caducidad automática del bloqueo, incluso si no se elimina, se eliminará automáticamente.

Etapa 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();
        }​​​​​​​

Problemas: 1. Setnx está configurado y cuando estoy a punto de configurar el tiempo de vencimiento, falla. Estancado de nuevo. Solución: la configuración del tiempo de vencimiento y los marcadores de posición debe ser atómica. redis admite el uso del comando 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();
    }
}

Pregunta: 1. ¿Eliminar el bloqueo directamente? ? ? Si el bloqueo en sí expira debido a un tiempo comercial prolongado y lo eliminamos directamente, podemos eliminar el bloqueo que otros tienen. Solución: cuando ocupe un candado, especifique el valor como uuid y todos lo eliminarán solo si coincide con su propio candado.

etapa cuatro

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();
        }
    }


Preguntas: 1. Si resulta ser el valor actual, cuando está a punto de eliminar el bloqueo, el bloqueo ha caducado y alguien más lo ha establecido en un nuevo valor. Entonces lo que eliminamos es el bloqueo de otra persona Solución: eliminar el bloqueo debe garantizar la atomicidad. Completado usando el script redis+Lua

Etapa cinco: forma final

 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 es una cuadrícula de datos en memoria de Java (In-Memory Data Grid) implementada sobre la base de Redis. No solo proporciona una serie de objetos Java comunes distribuidos, sino que también proporciona muchos servicios distribuidos. Estos incluyen (BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publicar/Suscribir, Filtro Bloom, Servicio remoto, Spring cache, Servicio Ejecutor, Servicio Live Object , Servicio de programación) Redisson proporciona la forma más sencilla y cómoda de utilizar Redis. El propósito de Redisson es promover la separación de preocupaciones de los usuarios sobre Redis (Separación de preocupaciones), para que los usuarios puedan concentrarse más en procesar la lógica empresarial.

 

 

Supongo que te gusta

Origin blog.csdn.net/guoweiting/article/details/115379589
Recomendado
Clasificación