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

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 分布式锁 (文末有项目连接))