zoukankan      html  css  js  c++  java
  • Redis分布式锁,基于StringRedisTemplate和基于Lettuce实现setNx

    使用redis分布式锁,来确保多个服务对共享数据操作的唯一性
    一般来说有StringRedisTemplate和RedisTemplate两种redis操作模板。

    根据key-value的类型决定使用哪种模板,如果k-v均是String类型,则使用StringRedisTemplate,否则使用RedisTemplate

    redis加锁操作
    必须遵循原子性操作,保证加锁的唯一性
    核心方法
    set(lockKey,value,"NXXX","EXPX",expireTime)
    NXXX:只能取NX或者XX,NX-key不存在时进行保存,XX-key存在时才进行保存
    EXPX:过期时间单位 (EX,PX),EX-秒,PX-毫秒

    使用StringRedisTemplate实现加锁

    public class StringRedisTemplateImplClient {
        // NX,XX
        //NX-key不存在则保存,XX-key存在则保存
        private static final String STNX= "NX";
    
        //EX,PX
        //EX-秒,PX-毫秒
        private static final String SET_EXPIRE_TIME = "PX";
    
        private RedisTemplate redisTemplate;
        private StringRedisTemplate stringRedisTemplate;
        
    
        public StringRedisTemplateImplClient(RedisTemplate redisTemplate){
            this.redisTemplate = redisTemplate;
            this.stringRedisTemplate = new StringRedisTemplate();
            this.stringRedisTemplate.setConnectionFactory(redisTemplate.getConnectionFactory());
            this.stringRedisTemplate.afterPropertiesSet();
        }
    
        public StringRedisTemplateImplClient(StringRedisTemplate redisTemplate){
            this.stringRedisTemplate = redisTemplate;
        }
        
        public boolean addRedisLock(String lockKey,String requestId,long expireTime){
            boolean result = stringRedisTemplate.opsForValue()
                .setIfAbsent(lockKey,lockKey,expireTime, TimeUnit.SECONDS);
            return result;
        }
    }

    下面简要分析下这个方法的一致性,查看setIfAbsent的源码

    setIfAbsent这个方法是spring-data-redis提供的,分析其源码,实现类为org.springframework.data.redis.core.DefaultValueOperations
    方法如下:
    key-需要加锁的key
    value-需要加锁的value
    timeout-失效的时间
    timeunit-常量,指定失效的时间单位

    connection默认为lettuce驱动连接(springboot 2.0以后的版本)

    public Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit) {
        byte[] rawKey = this.rawKey(key);
        byte[] rawValue = this.rawValue(value);
        Expiration expiration = Expiration.from(timeout, unit);
        return (Boolean)this.execute((connection) -> {
            return connection.set(rawKey, rawValue, expiration, SetOption.ifAbsent());
        }, true);
    }
    

    其中SetOption为指定NX/XX类型

    public static enum SetOption {
        UPSERT,
        SET_IF_ABSENT,
        SET_IF_PRESENT;
    
        private SetOption() {
        }
    
        public static RedisStringCommands.SetOption upsert() {
            return UPSERT;
        }
    
        public static RedisStringCommands.SetOption ifPresent() {
            return SET_IF_PRESENT;
        }
    
        public static RedisStringCommands.SetOption ifAbsent() {
            return SET_IF_ABSENT;
        }
    }

    查询spring官网查询这三个属性

    SET_IF_ABSENT--->NX
    SET_IF_PRESENT--->XX

    自定义实现SetNx

    setNx+expireTime实现加锁

     核心代码

    String status = stringRedisTemplate.execute(new RedisCallback<String>() {
      @Override
      public String doInRedis(RedisConnection connection) throws DataAccessException {
        Object nativeConnection = connection.getNativeConnection();
        String status = null;
        RedisSerializer<String> stringRedisSerializer = (RedisSerializer<String>) stringRedisTemplate.getKeySerializer();
    
        byte[] keyByte = stringRedisSerializer.serialize(key);
        //springboot 2.0以上的spring-data-redis 包默认使用 lettuce连接包
    
        //lettuce连接包,集群模式,ex为秒,px为毫秒
        if (nativeConnection instanceof RedisAdvancedClusterAsyncCommands) {
          logger.debug("lettuce Cluster:---setKey:"+setKey+"---value"+value+"---maxTimes:"+expireSeconds);
          status = ((RedisAdvancedClusterAsyncCommands) nativeConnection)
              .getStatefulConnection().sync()
              .set(keyByte,keyByte,SetArgs.Builder.nx().ex(30));
          logger.debug("lettuce Cluster:---status:"+status);
        }
        //lettuce连接包,单机模式,ex为秒,px为毫秒
        if (nativeConnection instanceof RedisAsyncCommands) {
          logger.debug("lettuce single:---setKey:"+setKey+"---value"+value+"---maxTimes:"+expireSeconds);
          status = ((RedisAsyncCommands ) nativeConnection)
          .getStatefulConnection().sync()
          .set(keyByte,keyByte, SetArgs.Builder.nx().ex(30));
          logger.debug("lettuce single:---status:"+status);
        }
        return status;
      }
    });
    logger.debug("getLock:---status:"+status);//执行正确status="OK"
  • 相关阅读:
    Live2D 看板娘
    Live2D 看板娘
    python框架☞Flask
    Python框架☞Django
    Python ORM框架之SQLALchemy
    HTTP1.0 HTTP 1.1 HTTP 2.0主要区别
    【F12】修改 DevTools的主题
    【F12】网络面板
    【正在等待可用套接字】解决方法
    nginx反向代理压测问题记录
  • 原文地址:https://www.cnblogs.com/ClareZjy/p/10448791.html
Copyright © 2011-2022 走看看