美文网首页
RateLimiter限流

RateLimiter限流

作者: Allen丶Joe | 来源:发表于2018-05-12 00:32 被阅读0次

一、常见的限流算法

1.漏桶算法

漏桶算法的原理比较简单,请求进入到漏桶中,漏桶以一定的速率漏水。当请求过多时,水直接溢出。可以看出,漏桶算法可以强制限制数据的传输速度。

image
2.令牌桶算法

令牌桶算法的原理是系统以一定速率向桶中放入令牌,如果有请求时,请求会从桶中取出令牌,如果能取到令牌,则可以继续完成请求,否则等待或者拒绝服务。这种算法可以应对突发程度的请求,因此比漏桶算法好。

image

在 Wikipedia 上,令牌桶算法是这么描述的:

  • 每秒会有 r 个令牌放入桶中,或者说,每过 1/r 秒桶中增加一个令牌
  • 桶中最多存放 b 个令牌,如果桶满了,新放入的令牌会被丢弃
  • 当一个 n 字节的数据包到达时,消耗 n 个令牌,然后发送该数据包
  • 如果桶中可用令牌小于 n,则该数据包将被缓存或丢弃

二、RateLimiter

Guava中开源出来一个令牌桶算法的工具类RateLimiter,可以轻松实现限流的工作。RateLimiter 对简单的令牌桶算法做了一些工程上的优化,具体的实现是 SmoothBursty。需要注意的是,RateLimiter 的另一个实现 SmoothWarmingUp,就不是令牌桶了,而是漏桶算法。也许是出于简单起见,RateLimiter 中的时间窗口能且仅能为 1s,如果想搞其他时间单位的限流,只能另外造轮子。

RateLimiter 有一个有趣的特性是「前人挖坑后人跳」,也就是说 RateLimiter 允许某次请求拿走超出剩余令牌数的令牌,但是下一次请求将为此付出代价,一直等到令牌亏空补上,并且桶中有足够本次请求使用的令牌为止。这里面就涉及到一个权衡,是让前一次请求干等到令牌够用才走掉呢,还是让它先走掉后面的请求等一等呢?Guava 的设计者选择的是后者,先把眼前的活干了,后面的事后面再说。

测试代码:

public class RateLimiterMain {
    public static void main(String[] args) {
        RateLimiter rateLimiter = RateLimiter.create(2);
        System.out.println(rateLimiter.acquire(5));
        System.out.println(rateLimiter.acquire(2));
        System.out.println(rateLimiter.acquire(1));
    }
}

输出内容:

2.496889
0.992149

可以看出,令牌桶每秒只能产生2个令牌,我们可以第一次取出5个,但是第二个再去取令牌的时候,需要等2.5s,也就是第一次令牌取完后,需要等2.5s才能取到令牌。同样的,第三次取1个令牌的时候,也需要等待第二次的1s的时间。也就是,取的速率可以超过令牌产生的速率,但是下一次再次去取的时候,需要阻塞等待。
当然也可以使用tryAcquire来非阻塞的获取,可以实时返回结果。另外tryAcquire也可以传入参数,也就是等待的时间,超时直接返回false。这点等同于常见的lock,tryLock。

三、RateLimiter对API接口的限流实例

  • pom文件代码
        <!-- google  guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>16.0.1</version>
        </dependency>
  • java代码
package com.infinite.controller;

import java.util.concurrent.TimeUnit;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.google.common.util.concurrent.RateLimiter;
import com.infinite.controller.vo.ResultBean;

/**
 * 
* @ClassName: RateLimiterTestController
* @Description: RateLimiter限流(单应用,分布式考虑redis实现)
* @author chenliqiao
* @date 2018年5月11日 下午4:19:35
*
 */
@RestController
public class RateLimiterTestController {
    
    /**令牌桶容量,一秒生成5个令牌**/
    private static final double PERMITS=5;
    
    RateLimiter rateLimiter=RateLimiter.create(PERMITS);
    
    @RequestMapping(value="/rateLimiter/getOrderNO")
    public ResultBean<String> getOrderNO(){
        /** 
         * tryAcquire(long timeout, TimeUnit unit) 
         * 从RateLimiter 获取许可如果该许可可以在不超过timeout的时间内获取得到的话, 
         * 或者如果无法在timeout 过期之前获取得到许可的话,那么立即返回false(无需等待) 
         */  
        if(!rateLimiter.tryAcquire(1000, TimeUnit.MILLISECONDS)){
            System.out.println("获取失败!");
            return new ResultBean<>("error!");
        }
        
        String orderNo="NO"+System.currentTimeMillis();
        System.out.println("获取成功:"+orderNo);
        return new ResultBean<String>(orderNo);
    }

}

相关文章

网友评论

      本文标题:RateLimiter限流

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