redis分布式锁_Redis 分布式锁、限流

本文介绍了分布式锁的两种常见实现方案:Redis和Zookeeper。重点讲解了Redis实现分布式锁的细节,包括尝试获取锁的逻辑、锁的释放以及防止锁超时导致的原子性问题。此外,还探讨了Redis的限流策略,提到了令牌桶和漏桶算法,并给出了Lua脚本示例。最后,提到了Redisson作为解决Redis锁超时问题的解决方案。
摘要由CSDN通过智能技术生成

  谈及分布式系统的时候,很多企业都会使用到分布式锁,分布式锁的实现方案目前主要是存在两种,redis实现和zookeeper实现。

  redis实现方案是多个线程相互竞争,在redis的某一个节点内创建一个键值对,谁能创建成功谁就获取到了锁。锁的释放就是删除当前线程创建的键值对,交给剩余线程去争抢。

   /**     * 应该有两个时间  一个是redis中的键失效时间   一个是执行是循环的跳出时间     * @param key     * @param expire     * @return     */    @Override    public Lock tryLock(String key, int expire, int waitime) {        Boolean success;        Long start = System.currentTimeMillis();        Long end = System.currentTimeMillis() + (waitime * 1000);        Long  number = 0L;        Lock lock = new Lock(Lockeys+key , UUID.randomUUID().toString() , 0L);        while(true){            success = redisTemplate.opsForValue().setIfAbsent(lock.getKey(),lock.getValue(),expire, TimeUnit.SECONDS);            if(success == null){                //此处应该抛出异常 获取锁失败                throw  new RuntimeException("获取锁失败");            }            if(success){                break;            }            //等待时间太长自动释放            Long curtime = System.currentTimeMillis();            if(curtime>end){                break;            }            try {                Thread.sleep(100);  //休眠100毫秒                number++;//也可以根据自旋的次数来选择释放            } catch (InterruptedException e) {                e.printStackTrace();            }        }        Long locktime = System.currentTimeMillis();        lock.setLocktime(locktime-start);        lock.setFalg(success);        return lock;    }

  tryLock方法试图去获取锁,获取成功返回true,获取失败返回false。上面的代码主要的封装一个Lock对象,对象中存在的属性是键值对、锁的获取时间等。请求redis的原子操作类去试图创建一个有限时间的键值对。创建成功的线程返回true执行后续的业务逻辑,未获取锁的线程不断在轮询去请求redis试图获取锁,所以线程设置了休眠100毫秒,减少不必要的线程不断的轮询redis。不断轮询的线程不可能一致让它阻塞在此,所以我们这边设置了一个时间,线程等待一定的时间未拿到锁之后,直接返回false。

锁释放:

 /**     * 为了防止锁释放的不是当前锁,需要校验value     * @param lock     * @param     * @return     */    @Override    public Boolean unlock(Lock lock) {        String value = (String) redisTemplate.opsForValue().get(lock.getKey());        if(value.equals(lock.getValue())){           return redisTemplate.delete(lock.getKey());        }else {            return false;        }    }

  锁的释放其实就是在redis中删除当前线程创建的键值对,怎么保证删除对应的键值对是当前线程创建的呢?我们这边比较了一个key值对应的value值,只有两者相同才会执行删除操作。

    使用redis的分布式锁绕不开的一个问题就是业务逻辑执行超时后,锁自动释放,所以就无法保证原子性操作。Redis的Liberary中提供了一个第三方的插件Redisson。其大致流程如图所示:

56589d39d29871d0406452797b05da95.png

    redisson存在一个watch(看门狗)机制,当前业务逻辑执行超时还未释放锁的时候,会定期延长锁的超时时间。

限流对于分布式系统而言是绕不开的问题,redis提供的几种限流的实现方式

(lua脚本实现原子操作)

限流算法:令牌桶算法、漏桶算法

  令牌桶算法:存在一个定时任务不断的向桶里面发送令牌,桶里面溢出的话,令牌会丢弃。

   一个请求进来会首先去桶中获取令牌,令牌可以获取多个,获取到令牌后才能转发请求,未获取到令牌,请求丢弃。

35d67e38b52d436f9aa212327dc40fd6.png

漏桶算法:

    以一定的速率向桶中放置令牌,然后一定的速率流出。突发的大批量请求不适合使用漏桶算法。

f90fb91abf6e2e124dbf83e2e49ae475.png

lua脚本,通过lua实现redis令牌增加的原子操作

--lua 下标从 1 开始-- 限流 keylocal key = KEYS[1]-- 限流大小local limit = tonumber(ARGV[1])-- 获取当前流量大小local curentLimit = tonumber(redis.call('get', key) or "0")if curentLimit + 1 > limit then    -- 达到限流大小 返回    return 0;else    -- 没有达到阈值 value + 1    redis.call("INCRBY", key, 1)    -- EXPIRE后边的单位是秒    redis.call("EXPIRE", key, 10)    return curentLimit + 1end

        入参key值和limit限制,如若操作数量则返回0,没有超过限制则Key对应得数量+1。设置key值得过期时间10秒。

1c2e75df1bc97ee41c2ebf9f09d771ad.png

       我是凯腾凯,互联网浪潮下一枚苟且偷生的程序员

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值