不管单机还是集群的限流实现已经给你准备好了

编程

限流算法

  • 计数器算法

    维护一个counter,规定在单位时间内counter的大小不能超过最大值,每隔固定时间就将counter的值置零。如果这个counter大于设定的阈值,那么系统就拒绝请求

  • 漏桶算法

    维护一个固定容量的桶,这个桶会按照指定的速度漏水。请求到达系统就类似于将水加入桶中,这个速度可以是匀速的也可以是瞬间的,如果这个桶满了,就会忽略后面的请求,直到这个桶可以存放多余的水。

    好处:可以将系统的处理能力维持在一个比较平稳的水平

    缺点:瞬间流量过来时,如果满了就会拒绝后面的请求流量

  • 令牌桶算法

    系统按照指定的速度往桶中添加token,每来一个请求,就从桶里拿走一个token,如果没有token就拒绝服务

    好处:控制系统的处理速度,通过统计信息实时优化令牌桶的大小

    与漏桶算法的区别:

    令牌桶算法由于在令牌桶里攒了很多令牌,因此在大流量到达的瞬间可以一次性将队列中所有的请求都处理完,然后按照恒定的速度处理请求

    漏桶算法是一直有一个恒等阈值,在大流量到达的时候,也会将多余的请求拒绝

限流实践

guava的concurrent中有一个限流工具类RateLimiter,其实现了单机限流,使用了令牌桶算法,它支持两种令牌获取接口:获取不到一直阻塞;在指定时间内获取不到就阻塞,超过这个时间就返回获取失败

  1. 使用RateLimiter,单机限流

     //初始化令牌桶大小,初始大小为2000

     private RateLimiter ratelimiter = RateLimiter.create(2000);

     public void process() {

         //获取令牌,获取不到就阻塞

         rateLimiter.acquire();

         //执行业务操作,例如写数据库

         bizLogic();

     }

    如果请求可以丢弃,防止大量请求过来耗尽系统资源,可以使用tryAcquire()方法,和带超时的tryAcquire(),在指定时间内获取不到令牌就返回false

     private RateLimiter rateLimiter = RateLimiter.create(2000);

     if(rateLimiter.tryAcquire()) {

         doSomething();

     } else {

         doSomethingElse();

     }

    RateLimiter 详细分析可见:https://www.jianshu.com/p/362d261115e7

  1. 使用Redis, 分布式限流

    比如1秒内请求数量限制在2000内

    • 使用Redis实现计数器算法

      设置redis可以的过期时间为1秒,每次请求过来value加1,如果value超过2000就拒绝访问,使用Lua脚本实现incr 和 expire的原子性

       local key =KEYS[1]

       local expire_time =ARGV[1]

       local count =redis.call("INCR", key, 1)

       if count == 1 then

           redis.call("EXPIRE", key, expire_time)

       end

       return count

       

    • 使用Redis实现令牌桶算法

      使用Redis的List结构实现

      定时任务执行,使用rightPush放入令牌,并保证令牌的唯一性

       // 1S的速率往令牌桶中添加UUID,只为保证唯一性

           @Scheduled(fixedDelay = 1000,initialDelay = 0)

           public void setIntervalTimeTask(){

               redisTemplate.opsForList().rightPush("limit_list",UUID.randomUUID().toString());

          }

      使用leftPop获取令牌

       public Response limitFlow(Long id){

           Object result = redisTemplate.opsForList().leftPop("limit_list");

           if(result == null){

               return Response.ok("当前令牌桶中无令牌");

          }

           return Response.ok(articleDescription);

       }

以上是 不管单机还是集群的限流实现已经给你准备好了 的全部内容, 来源链接: utcz.com/z/510912.html

回到顶部