老张开发 springboot下Redistemplate实现并发锁

谨以此记录学习redis并发锁学习笔记:

      基于传统的单机模式下的并发锁,已远远不能满足当下高并发大负载的情况,当下常用的并发处理如下

       1、使用synchronized关键字

        2、select    for update   乐观锁

        3、使用redis实现同步锁

方案一 适合单机模式,

方案二 虽然满足多节点服务实例但 对变更操作的吞吐量有影响

方案三 基于redis nosql数据库  在效率与横向扩展方面都大大优于前两种方案

redis  单线程   在自身设计上一定程度可避免想成不安全  再者其效率高于关系型数据库

本次实现锁机制  基于redis 的两个指令 查询 redis.cn  网站  

指令一:SETNX key value

key设置值为value,如果key不存在,这种情况下等同SET命令。 当key存在时,什么也不做。SETNX是”SET if Not eXists”的简写。

返回值

Integer reply, 特定值:

  • 1 如果key被设置了
  • 0 如果key没有被设置

例子

redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"
redis> 

 指令二:GETSET key value

自动将key对应到value并且返回原来key对应的value。如果key存在但是对应的value不是字符串,就返回错误。

返回值

bulk-string-reply: 返回之前的旧值,如果之前Key不存在将返回nil

例子

redis> INCR mycounter
(integer) 1
redis> GETSET mycounter "0"
"1"
redis> GET mycounter
"0"
redis> 

 步骤一:引入依赖

 
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            org.apache.commons
            commons-pool2
            2.6.0
        

步骤二: 配置redis连接

public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        RedisTemplate template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(lettuceConnectionFactory);
        return template;
    }

    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        return new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
                log.error("redis异常:key=[{}]", key, exception);
            }

            @Override
            public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
                log.error("redis异常:key=[{}]", key, exception);
            }

            @Override
            public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
                log.error("redis异常:key=[{}]", key, exception);
            }

            @Override
            public void handleCacheClearError(RuntimeException exception, Cache cache) {
                log.error("redis异常:", exception);
            }
        };
    }
}

 

 代码demo:将Redistemplate 注入需要使用的地方

public class RedisLock {
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 加锁
     * @param key   键
     * @param value 当前时间 + 超时时间
     * @return 是否拿到锁
     */
    public boolean lock(String key, String value) {
        if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
            return true;
        }
        String currentValue = redisTemplate.opsForValue().get(key);
        //如果锁过期
        if (!StringUtils.isEmpty(currentValue)
                && Long.parseLong(currentValue) < System.currentTimeMillis()) {
            String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
            //是否已被别人抢占
            return StringUtils.isNotEmpty(oldValue) && oldValue.equals(currentValue);
        }
        return false;
    }

    /**
     * 解锁
     *
     * @param key   键
     * @param value 当前时间 + 超时时间
     */
    public void unlock(String key, String value) {
        try {
            String currentValue = redisTemplate.opsForValue().get(key);
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        } catch (Exception e) {
            log.error("redis解锁异常");
        }
    }
}

使用demo:

 private void getJsapiTicketCatch(String appid, Map resultMap) {
        long expr = 90 * 60 * 1000L;
        long ex = 5 * 1000L;
        String value = String.valueOf(System.currentTimeMillis() + ex);
        boolean lock = redisLock.lock(appid, value);
        //获取锁
        if (lock) {
            String tick = redisTemplate.opsForValue().get("zqs"+appid);
            if (StringUtils.isNotBlank(tick)) {
                resultMap.put("tick",tick);
            } else {
                //token 通过appid
                BigAuthorizationInfo authorInfoByAppidService = authorizedService.getAuthorInfoByAppidService(appid);
                if (authorInfoByAppidService == null
                        || StringUtils.isBlank(authorInfoByAppidService.getAuthorizer_access_token())) {
                    resultMap.put("tick",null);
                }else {
                    String token = authorInfoByAppidService.getAuthorizer_access_token();
                    String jsapiTicket = WeShareUtils.GetJsapiTicket(token);
                    if (StringUtils.isBlank(jsapiTicket)) {
                        resultMap.put("tick",null);
                    }else {
                        resultMap.put("tick",jsapiTicket);
                        redisTemplate.opsForValue().set("zqs"+appid, jsapiTicket, expr,TimeUnit.MILLISECONDS);
                    }
                }
            }
            //释放锁
            redisLock.unlock(appid, value);
        }

 

你可能感兴趣的:(老张开发 springboot下Redistemplate实现并发锁)