转载自:https://www.jianshu.com/p/2fc92a929e8e
叙述
在平常项目中为了防止一些没有token访问的API被大量无限的调用,需要对一些服务进行API限流。就好比拿一些注册或者发验证码的一些接口,如果被恶意无限的调用,多少会造成一些费用的产生,发短信或者邮件都是一些第三方接口,次数越多,当然费用也就越多了,严重的直接导致服务崩溃。spring cloud api-gateway中引入限流的配置还是必须的。
解决方案
在pom文件中引入Zuul RateLimit的依赖
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-zuul-ratelimit</artifactId>
<version>1.3.2.RELEASE</version>
</dependency>
yml配置
更详细的配置解读下面有写,这里只是简单配置一下,以下这个配置就可以对服务进行限流了
zuul:
routes: 你的路由配置
test:
path:
serviceId:
ratelimit:
enabled: true
policies:
test: 路由名
limit: 限制次数
refresh-interval: 刷新时间
type: 类型
示列
本地让自己的一个服务配置为一分钟内该服务的API只能访问十次,超过十次,网关就会报错
zuul:
routes:
test:
path: /api/test/**
serviceId: hscf-cloud-test-9457
ratelimit:
enabled: true
policies:
test:
limit: 10
refresh-interval: 60
type: origin 限流方式
下面通过源码简要分析一下
RateLimit类是继承ZuulFilter,内中的变量不难看出就是我们在yml文件中配置的属性值。RateLimit内中的部分源码,filterType为“pre”表示在每一个API访问之前进行拦截,LIMIT_HEADER,REMAINING_HEADER,RESET_HEADER这三个变量应该就是获取我们配置的访问次数,还有记录该时间内剩余的访问次数。
public class RateLimitFilter extends ZuulFilter {
public static final String LIMIT_HEADER = "X-RateLimit-Limit";
public static final String REMAINING_HEADER = "X-RateLimit-Remaining";
public static final String RESET_HEADER = "X-RateLimit-Reset";
public String filterType() {
return "pre";
}
public int filterOrder() {
return -1;
}
public boolean shouldFilter() {
return this.properties.isEnabled() && this.policy(this.route()).isPresent();
}
主体逻辑run()中进行判断。先通过this.policy(route).ifPresent((policy)判断policy配置信息是否存在,存在的话会读
取到当前的限制值,还剩余的限制值,最终判断剩余的限制值是否小于0,小于0的话就会报出太多请求的异常
TOO_MANY_REQUESTS(429, "Too Many Requests")
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletResponse response = ctx.getResponse();
HttpServletRequest request = ctx.getRequest();
Route route = this.route();
this.policy(route).ifPresent((policy) -> {
String key = this.rateLimitKeyGenerator.key(request, route, policy);
Rate rate = this.rateLimiter.consume(policy, key);
response.setHeader("X-RateLimit-Limit", policy.getLimit().toString());
response.setHeader("X-RateLimit-Remaining", String.valueOf(Math.max(rate.getRemaining().longValue(), 0L)));
response.setHeader("X-RateLimit-Reset", rate.getReset().toString());
if(rate.getRemaining().longValue() < 0L) {
ctx.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
ctx.put("rateLimitExceeded", "true");
throw new ZuulRuntimeException(new ZuulException(HttpStatus.TOO_MANY_REQUESTS.toString(), HttpStatus.TOO_MANY_REQUESTS.value(), (String)null));
}
});
return null;
}
。。。。。。
控制台的异常信息,异常code为429,也就是太多请求的异常:TOO_MANY_REQUESTS(429, "Too Many Requests")
RateLimit详细的配置信息解读
zuul:
ratelimit:
key-prefix: your-prefix #对应用来标识请求的key的前缀
enabled: true
repository: REDIS #对应存储类型(用来存储统计信息)
behind-proxy: true #代理之后
default-policy: #可选 - 针对所有的路由配置的策略,除非特别配置了policies
limit: 10 #可选 - 每个刷新时间窗口对应的请求数量限制
quota: 1000 #可选- 每个刷新时间窗口对应的请求时间限制(秒)
refresh-interval: 60 # 刷新时间窗口的时间,默认值 (秒)
type: #可选 限流方式
- user
- origin
- url
policies:
myServiceId: #特定的路由
limit: 10 #可选- 每个刷新时间窗口对应的请求数量限制
quota: 1000 #可选- 每个刷新时间窗口对应的请求时间限制(秒)
refresh-interval: 60 # 刷新时间窗口的时间,默认值 (秒)
type: #可选 限流方式
- user
- origin
- url
- url类型的限流就是通过请求路径区分
- origin是通过客户端IP地址区分
- user是通过登录用户名进行区分,也包括匿名用户