redis实现分布式锁

环境:

springboot 2.0.4

maven

一、在pom中添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

二、在application.yml下填写redis的配置

spring.redis.host: 127.0.0.1

spring.redis.database: 0

三、 自定义redistemplate,指定序列化的方式,防止后面出现序列化的错误

@Configuration
public class RedisConf {

    @Bean
    public RedisTemplate<String, Object> redisTemplate( RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(factory);
        RedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(stringRedisSerializer);
        return redisTemplate;
    }
}

四、实现方法

/*
* redis高并发击穿的处理,采用分布式锁
* 击穿是指:就是某一个热点数据,缓存中某一时刻失效了,因而大量并发请求打到数据库上,就像被击穿了一样。说白了,就是某个数据,数据库有,但是缓存中没有。
* */
@RestController
@CrossOrigin
public class LogFacade {

    private  static Logger logger = LoggerFactory.getLogger(LogFacade.class);

    @Resource
    private RedisTemplate redisTemplate;

    private static final Long SUCCESS = 1L;


    @GetMapping("/getTest")
    public String getTest() throws InterruptedException {
    	// 从redis中获取一个值
        String str = (String) redisTemplate.opsForValue().get("1");
        JSONObject result = null;
        //	当值被获取且不为空,直接返回结果
        if(!StringUtils.isEmpty(str)){
            return str;
        }else{
            String uuid = UUID.randomUUID().toString();
            //获取锁
            Boolean lock = getLock("lock",uuid,100);
            System.out.println("lock"+lock);
            //加锁成功则进行相关操作
            if(lock == true){
            	再次尝试从redis中取值,不为空则返回
                String str2 = (String) redisTemplate.opsForValue().get("1");
                if(!StringUtils.isEmpty(str2)){
                    releaseLock("lock",uuid);
                    return str2;
                }
                JSONObject obj = ... // 从数据库获取数据
                System.out.println("击穿至数据库");
                SetParams setParams = new SetParams();
                setParams.ex(10);
                //数据保存至redis
                redisTemplate.opsForValue().set("1",obj.toJSONString(),100, TimeUnit.SECONDS);
                result = obj;
                // 解锁
                Boolean unlock = releaseLock("lock",uuid);
                System.out.println("unlock"+unlock);
                return result.toJSONString();
            }else{
            	// 未能获取锁的,在等待一会后进行重试
                Thread.sleep(500);
                return getTest();
            }
        }

    }

	// 注意采用这种方式去创建锁是为了保证锁的原子性,避免出现死锁(重要)
    /**
     * 获取锁
     * @param lockKey
     * @param value
     * @param expireTime:单位-秒
     * @return
     */
    public  boolean getLock(String lockKey, String value, int expireTime){
        boolean ret = false;
        try{
            String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";

            RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
            Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),value, expireTime+"");

            if(SUCCESS.equals(result)){
                return true;
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return ret;
    }

	// 解锁时保证加锁人和解锁人是一致的
    /**
     * 释放锁
     * @param lockKey
     * @param value
     * @return
     */
    public  boolean releaseLock(String lockKey, String value) {

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);

        Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), value);
        if (SUCCESS.equals(result)) {
            return true;
        }

        return false;
    }


}
posted @ 2021-07-30 15:19  cxylm  阅读(70)  评论(0)    收藏  举报