Bloqueios distribuídos + problemas de cache distribuído usam redis (redisson) para implementar bloqueios distribuídos

Bloqueio distribuído + problema de cache distribuído

  • No modo de microsserviço, vários serviços idênticos precisam manter a consistência dos dados para o banco de dados. Neste momento, ele precisa evoluir de um bloqueio local para um bloqueio distribuído.
  • Esta postagem do blog continuamente levanta questões e resolve ideias de uma forma avançada, melhora o código passo a passo e realiza a função de bloqueio distribuída com alta confiabilidade.

Insira a descrição da imagem aqui

Use o comando redis set com o parâmetro NX (não existe) para realizar o bloqueio distribuído

NX: somente quando não existe, pode ser definido; conjunto bem-sucedido retornará OK, retorno nulo sem sucesso
Insira a descrição da imagem aqui

//分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
    
    

    //1、占分布式锁。去redis占坑
    Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock");
    if(aBoolean){
    
    
        //加锁成功 执行业务
        Map<String, List<Catelog2Vo>> dataFromDB = this.getDataFromDB();
        //删除锁
        stringRedisTemplate.delete("lock");
        return dataFromDB;
    }else {
    
    
        //加锁失败   重试 自旋
        return getCatalogJsonFromDBWithRedisLock();
    }
}

Fase dois independente mais o tempo de expiração do bloqueio distribuído

Insira a descrição da imagem aqui

//分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
    
    

    //1、占分布式锁。去redis占坑
    Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock");
    if(aBoolean){
    
    
        //加锁成功 执行业务
        //2、设置过期时间
        stringRedisTemplate.expire("lock",30, TimeUnit.SECONDS);
        Map<String, List<Catelog2Vo>> dataFromDB = this.getDataFromDB();
        //删除锁
        stringRedisTemplate.delete("lock");
        return dataFromDB;
    }else {
    
    
        //加锁失败   重试 自旋
        return getCatalogJsonFromDBWithRedisLock();
    }
}

Fase três bloqueio atômico e tempo de expiração definido

Insira a descrição da imagem aqui

//分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
    
    

    //1、占分布式锁。去redis占坑  并设置过期时间 必须是同步的 原子的
    Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock",30,TimeUnit.SECONDS);
    if(aBoolean){
    
    
        //加锁成功 执行业务
        Map<String, List<Catelog2Vo>> dataFromDB = this.getDataFromDB();
        //删除锁
        stringRedisTemplate.delete("lock");
        return dataFromDB;
    }else {
    
    
        //加锁失败   重试 自旋
        return getCatalogJsonFromDBWithRedisLock();
    }
}

Fase 4 excluir bloqueio para permissão uuid correspondência

Insira a descrição da imagem aqui

//分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
    
    

    //1、占分布式锁。去redis占坑  并设置过期时间 必须是同步的 原子的
    String uuid = UUID.randomUUID().toString();
    Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock",uuid,30,TimeUnit.SECONDS);
    if(aBoolean){
    
    
        //加锁成功 执行业务
        Map<String, List<Catelog2Vo>> dataFromDB = this.getDataFromDB();
        String lock = stringRedisTemplate.opsForValue().get("lock");
        if(uuid.equals(lock)){
    
    
            //删除自己的锁
            stringRedisTemplate.delete("lock");
        }
        return dataFromDB;
    }else {
    
    
        //加锁失败   重试 自旋
        return getCatalogJsonFromDBWithRedisLock();
    }
}

Fase 5 do script Lua excluir a operação atômica do bloqueio

Insira a descrição da imagem aqui

//分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
    
    

    //1、占分布式锁。去redis占坑  并设置过期时间 必须是同步的 原子的
    String uuid = UUID.randomUUID().toString();
    Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock",uuid,30,TimeUnit.SECONDS);
    if(aBoolean){
    
    
        //加锁成功 执行业务
        Map<String, List<Catelog2Vo>> dataFromDB = this.getDataFromDB();
        //获取值 + 对比 + 删除 必须是原子操作  lua脚本解锁
        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                "then " +
                "   return  redis.call('del', KEYS[1])" +
                "else " +
                "   return 0 " +
                "end";
        Long result = stringRedisTemplate.execute(new DefaultRedisScript<Long>(luaScript, Long.class), Arrays.asList("lock"), uuid);
        
        return dataFromDB;
    }else {
    
    
        //加锁失败   重试 自旋
        return getCatalogJsonFromDBWithRedisLock();
    }
}

Resultado final da fase 6

Independentemente de o negócio ser concluído corretamente, exclua os bloqueios estabelecidos por você

    //分布式锁
    public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
    
    

        //1、占分布式锁。去redis占坑  并设置过期时间 必须是同步的 原子的
        String uuid = UUID.randomUUID().toString();
        Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock",uuid,300,TimeUnit.SECONDS);
        if(aBoolean){
    
    
            //加锁成功 执行业务
            Map<String, List<Catelog2Vo>> dataFromDB = null;
            try {
    
    
                dataFromDB = this.getDataFromDB();
            }finally {
    
    
                //获取值 + 对比 + 删除 必须是原子操作  lua脚本解锁
                String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                        "then " +
                        "   return  redis.call('del', KEYS[1])" +
                        "else " +
                        "   return 0 " +
                        "end";
                Long result = stringRedisTemplate.execute(new DefaultRedisScript<Long>(luaScript, Long.class), Arrays.asList("lock"), uuid);
            }
            return dataFromDB;
        }else {
    
    
            //加锁失败   重试 自旋
            //睡眠
            try {
    
    
                Thread.sleep(200);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            return getCatalogJsonFromDBWithRedisLock();
        }
    }

Use a estrutura Redisson para implementar bloqueios distribuídos

Se você usar o redis manualmente para implementar bloqueios distribuídos, precisará escrever instruções de bloqueio atômico e script lua de desbloqueio atômico antes de cada processo de negócios, o que é muito complicado, e a estrutura de uso de bloqueios distribuídos Redisson pode ser mais conveniente de implementar. A camada inferior de Reddison implementa o pacote JUC Portanto, pode ser perfeitamente conectado ao uso do JUC.

Introduzir dependências

<!--引入redis的分布式锁  分布式对象框架redison-->
      <dependency>
          <groupId>org.redisson</groupId>
          <artifactId>redisson</artifactId>
          <version>3.12.0</version>
      </dependency>

Configurar o cliente redissonClient

@Configuration
public class MyRedissonConfig {
    
    
    /**
     * 所有对Redisson的使用 都是通过RedissonClient对象
     * @return
     * @throws IOException
     */
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() throws IOException{
    
    
        //1、创建配置   redisson包下的config
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.231.1:6379");
        //2、根据config创建出RedissonClient示例
        return Redisson.create(config);
    }
}

Use bloqueios de redisson para implementar bloqueios distribuídos

//分布式锁
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedissonLock() {
    
    

    //1、占分布式锁。redisson
    /**
     * 锁的名字 决定了锁的粒度,越细越快
     * 锁的粒度约定:  具体缓存的是某个数据  例如 11号商品  product-11-lock
     */
    RLock lock = redissonClient.getLock("catalogJson-lock");
    lock.lock();
    Map<String, List<Catelog2Vo>> dataFromDB = null;
    try{
    
    
        //加锁成功 执行业务
        dataFromDB = this.getDataFromDB();
    }finally{
    
    
        lock.unlock();
    }
    return dataFromDB;
}

Acho que você gosta

Origin blog.csdn.net/weixin_44634197/article/details/108308395
Recomendado
Clasificación