美文网首页
4:Redis 分布式锁 (文末有项目连接)

4:Redis 分布式锁 (文末有项目连接)

作者: _River_ | 来源:发表于2021-04-13 01:38 被阅读0次
    1:什么是缓存分布式锁
    首先这是一个锁 那么就是应对并发使用的
    然后它是分布式 那意味着这个锁可以在一个服务上锁 然后锁住另一个服务的逻辑
    最后它是缓存   那代表着这个锁效率十分快同时具有失效的时间
    
    可应用于防止用户重复下单
    
    2:分布式锁的关键代码
    //主要有两点是非常核心的
    //1:根据key 判断该锁是否已经存在了
    //2:该key需要设置过期时间
    
    @Slf4j
    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class RedisDistributedLockJunit {
        @Autowired
        StringRedisTemplate redisTemplate;
    
        @Test
        public void redisLockTest() {
    
            RedisConnection connection = null;
            connection = redisTemplate.getConnectionFactory().getConnection();
    
            String key = "member:name:HSJ";
            byte[] content ="content".getBytes();
    
            //关键点1:如果该key已经存在 则返回false  同时不会修改其内容
           boolean isExist =  connection.setNX(key.getBytes(), content);
           System.out.println("是否已经存在:"+isExist);
    
            //注意保证使用的key是存在的
            //redisTemplate.expire(key,timeout,timeunit);
            //参数说明  key 需要设置的key  timeout:key的生存时间  timeuint:时间单位(小时,分钟,秒……)
            //TimeUnit.MILLISECONDS 是毫秒
    
            // 关键点2:重置过期时间
            redisTemplate.expire(key, 15000L, TimeUnit.MILLISECONDS);
    
            //获取过期时间
            System.out.println("获取过期时间:"+redisTemplate.getExpire(key));
        }
    }
    
    
    3:业务代码使用分布式缓存锁
    @RestController
    @RequestMapping("/order")
    @Slf4j
    public class RedisDistributedLockController {
    
        @Autowired
        private RedisDistributedLockComponent redisDistributedLockComponent;
    
        @PostMapping(value = "/member/create")
        public String orderPay(Long memberId) {
    
            String memberOrderLockKey = "member:createOrder:" + memberId;
            //该锁会锁住这个下单的用户ID5秒
            boolean memberLock = redisDistributedLockComponent.getLock(memberOrderLockKey, 2000L, 5000L);
    
            //如果memberLock 为 false 则失败
            if (!memberLock) {
                return "fail  请不要重复下单";
            }
            try {
                //下单耗时3秒操作
                System.out.println(new Date());
                Thread.sleep(3000L);
                System.out.println(new Date());
    
            } catch (Exception e) {
                log.error("创建预授权订单,用户ID:{},异常:{}", memberId, e.getMessage(), e);
            } finally {
                boolean isRelease = redisDistributedLockComponent.releaseLock(memberOrderLockKey);
                if (!isRelease) {
                    log.error("该key的分布式锁释放失败{}", memberOrderLockKey);
                }
            }
            return "success  下单成功";
        }
    }
    
    
    4:业务代码使用分布式缓存锁
    @Component
    @Slf4j
    public class RedisDistributedLockComponent {
    
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        /**
         * 分布式锁
         */
    //    public class DistributedLock {
    
            //锁超时时间 4秒
            private static final long DEFAULT_LOCK_TIME_OUT = 4000L;
            //请求超时时间5秒
            private static final long DEFAULT_ACQUIRE_TIME_OUT = 5000L;
    
            /**
             * 获取锁
             * @param key
             * @param acquireTimeout  不断重试添加锁 直到超时 或者成功
             * @param lockTimeOut     如果添加锁成功 该锁的有效时间
             * @return
             */
            public boolean getLock(String key, long acquireTimeout, long lockTimeOut) {
                //初始化锁为 false
                boolean lock = false;
                key = "lock:" + key;
                long now = System.currentTimeMillis();
                acquireTimeout = now + acquireTimeout;
                //如果入参acquireTimeout 小于等于 0  则使用默认值
                if (0L>= acquireTimeout ) {
                    acquireTimeout = now + DEFAULT_ACQUIRE_TIME_OUT;
                }
                //如果入参lockTimeOut 小于等于 0  则使用默认值
                if (0 >= lockTimeOut) {
                    lockTimeOut = DEFAULT_LOCK_TIME_OUT;
                }
                //只要是字符串就行了content
                byte[] content ="content".getBytes();
                RedisConnection connection = null;
                try {
                    //获取redis的连接
                    connection = stringRedisTemplate.getConnectionFactory().getConnection();
    
                    //在acquireTimeout(请求锁超时时间内) 一直尝试添加锁 200L(0.2秒) 一次
                    //超过时间则添加锁失败
                    do {
                        //原子性 SET if Not Exists
                        if (connection.setNX(key.getBytes(), content)) {
                            //设置该key对应的锁超时时间
                            stringRedisTemplate.expire(key, lockTimeOut, TimeUnit.MILLISECONDS);
                            //设置锁为true
                            lock = true;
                            break;
                        }
                        Thread.sleep(200L);
                        log.info("key : {} 等待锁", key);
                    } while (System.currentTimeMillis() <= acquireTimeout);
                } catch (Exception e) {
                    log.error("获取锁异常", e);
                    return false;
                } finally {
                    if (null != connection) {
                        connection.close();
                    }
                }
                return lock;
            }
    
            /**
             * 释放锁
             * @param key
             * @return
             */
            public boolean releaseLock(String key) {
                boolean releaseLock = false;
                key = "lock:" + key;
                try {
                    stringRedisTemplate.delete(key);
                     releaseLock = true;
                } catch (Exception e) {
                    log.error("释放锁异常", e);
                    return false;
                }
                return releaseLock;
            }
    }
    
    5:测试
    使用postman 或者 Jmeter连续调用两次该接口就行
    
    先进来的获取分布式锁  该锁会锁住5秒 5秒内不会有其他人无法获取锁
    后进来的不断尝试获取锁   在2秒内 每0.2秒尝试获取一次
    
    执行逻辑的时间是3秒  执行逻辑这段时间锁(5秒)还在  重复请求无法重复进入该逻辑
    

    项目连接

    请配合项目代码食用效果更佳:
    项目地址:
    https://github.com/hesuijin/hesuijin-study-project
    Git下载地址:
    https://github.com.cnpmjs.org/hesuijin/hesuijin-study-project.git
    
    redis-module项目模块下

    相关文章

      网友评论

          本文标题:4:Redis 分布式锁 (文末有项目连接)

          本文链接:https://www.haomeiwen.com/subject/vbxqlltx.html