Zuul 网关限流---Guava RateLimiter

限流算法有漏桶算法和令牌桶算法,guava的RateLimiter使用的是令牌桶算法也就是以固定的频率向桶中放入令牌,例如一秒钟10枚令牌,实际业务在每次响应请求之前都从桶中获取令牌,只有取到令牌的请求才会被成功响应,获取的方式有两种:阻塞等待令牌或者取不到立即返回失败

image

Guava RateLimiter 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* @author haopeng
* @date 2019-07-16 20:17
*/
public class GuavaRateLimiterTest {

@Test
public void testAcquire() {
// acquire(i); 获取令牌,返回阻塞的时间,支持预消费.
RateLimiter limiter = RateLimiter.create(100);

for (int i = 1; i < 20; i++) {
double waitTime = limiter.acquire();
System.out.println("cutTime=" + longToDate(System.currentTimeMillis()) + " acq:" + i + " waitTime:" + waitTime);
}
}

public static String longToDate(long lo){
Date date = new Date(lo);
SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sd.format(date);
}


}

每秒1个令牌生成一个令牌,从输出可看出很平滑,这种实现将突发请求速率平均成固定请求速率。

Zuul中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Component
public class RateLimitFilter extends ZuulFilter {

private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);

@Override
public String filterType() {
return PRE_TYPE;
}

@Override
public int filterOrder() {
return SERVLET_DETECTION_FILTER_ORDER - 1;
}

@Override
public boolean shouldFilter() {
return true;
}

@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
// 如果获取不到令牌,进行拦截
if (!RATE_LIMITER.tryAcquire()) {
requestContext.setSendZuulResponse(false);
HttpStatus.TOO_MANY_REQUESTS.value()
requestContext.setResponseStatusCode(HttpStatus.BAD_REQUEST.value());
}
return null;
}
}


限流算法

1、计数器算法

计数器算法是限流算法里最简单也是最容易实现的一种算法。比如我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个。那么我们可以这么做:在一开 始的时候,我们可以设置一个计数器counter,每当一个请求过来的时候,counter就加1,如果counter的值大于100并且该请求与第一个 请求的间隔时间还在1分钟之内,那么说明请求数过多;如果该请求与第一个请求的间隔时间大于1分钟,且counter的值还在限流范围内,那么就重置 counter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class CounterTest {
public long timeStamp = getNowTime();
public int reqCount = 0;
public final int limit = 100; // 时间窗口内最大请求数
public final long interval = 1000; // 时间窗口ms

public boolean grant() {
long now = getNowTime();
if (now < timeStamp + interval) {
// 在时间窗口内
reqCount++;
// 判断当前时间窗口内是否超过最大请求控制数
return reqCount <= limit;
} else {
timeStamp = now;
// 超时后重置
reqCount = 1;
return true;
}
}

public long getNowTime() {
return System.currentTimeMillis();
}
}
滑动窗口

当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确

2、令牌桶算法

令牌桶算法是比较常见的限流算法之一,大概描述如下:

  • 1)、所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
  • 2)、根据限流大小,设置按照一定的速率往桶里添加令牌;
  • 3)、桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
  • 4)、请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
  • 5)、令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流;
3、漏桶算法