详解Spring Cloud Netflix Zuul中的速率限制
SpringCloudNetflixZuul是一个包含NetflixZuul的开源网关。它为SpringBoot应用程序添加了一些特定功能。不幸的是,开箱即用不提供速率限制。
除了SpringCloudNetflixZuul依赖项之外,我们还需要将SpringCloudZuulRateLimit添加到我们的应用程序的pom.xml中:
org.springframework.cloud spring-cloud-starter-netflix-zuul com.marcosbarbero.cloud spring-cloud-zuul-ratelimit 2.2.0.RELEASE
首先,让我们创建几个REST端点,我们将在其上应用速率限制。
下面是一个简单的SpringController类,有两个端点:
@Controller @RequestMapping("/greeting") publicclassGreetingController{ @GetMapping("/simple") publicResponseEntitygetSimple(){ returnResponseEntity.ok("Hi!"); } @GetMapping("/advanced") publicResponseEntity getAdvanced(){ returnResponseEntity.ok("Hello,howyoudoing?"); } }
让我们在application.yml文件中添加以下Zuul属性 :
zuul: routes: serviceSimple: path:/greeting/simple url:forward:/ serviceAdvanced: path:/greeting/advanced url:forward:/ ratelimit: enabled:true repository:JPA policy-list: serviceSimple: -limit:5 refresh-interval:60 type: -origin serviceAdvanced: -limit:1 refresh-interval:2 type: -origin strip-prefix:true
在zuul.routes下,我们提供端点详细信息。在zuul.ratelimit.policy-list下,我们为端点提供速率限制配置。该限属性指定的时间端点可以在内部被称为数字刷新间隔。
我们可以看到,我们为serviceSimple 端点添加了每60秒5个请求的速率限制。相比之下, serviceAdvanced的速率限制为每2秒1个请求。
该类型配置指定其速率限制的方法,以下是可能的值:
- origin-基于用户原始请求的速率限制
- url-基于下游服务的请求路径的速率限制
- user-基于经过身份验证的用户名或“匿名”的速率限制
- Novalue-充当每项服务的全局配置。要使用这种方法,请不要设置参数'type'
接下来,让我们测试一下速率限制:
@Test publicvoidwhenRequestNotExceedingCapacity_thenReturnOkResponse(){ ResponseEntityresponse=restTemplate.getForEntity(SIMPLE_GREETING,String.class); assertEquals(OK,response.getStatusCode()); HttpHeadersheaders=response.getHeaders(); Stringkey="rate-limit-application_serviceSimple_127.0.0.1"; assertEquals("5",headers.getFirst(HEADER_LIMIT+key)); assertEquals("4",headers.getFirst(HEADER_REMAINING+key)); assertEquals("60000",headers.getFirst(HEADER_RESET+key)); }
在这里,我们只对一个端点/greeting/simple进行一次调用。请求成功,因为它在速率限制内。
另一个关键点是,对于每个响应,我们返回标头Header,为我们提供有关速率限制的更多信息。对于上述请求,我们将获得以下标头:
X-RateLimit-Limit-rate-limit-application_serviceSimple_127.0.0.1:5 X-RateLimit-Remaining-rate-limit-application_serviceSimple_127.0.0.1:4 X-RateLimit-Reset-rate-limit-application_serviceSimple_127.0.0.1:60000
解释:
- X-RateLimit-Limit-[key]:为端点配置的限制
- X-RateLimit-Remaining-[key]: 调用端点的剩余尝试次数
- X-RateLimit-Reset-[key]:为端点配置的刷新间隔的剩余毫秒数
另外,如果我们再次立即触发相同的端点,我们可以得到:
X-RateLimit-Limit-rate-limit-application_serviceSimple_127.0.0.1:5 X-RateLimit-Remaining-rate-limit-application_serviceSimple_127.0.0.1:3 X-RateLimit-Reset-rate-limit-application_serviceSimple_127.0.0.1:57031
请注意减少的剩余尝试次数和剩余的毫秒数。
让我们看看当我们超过速率限制时会发生什么:
@Test publicvoidwhenRequestExceedingCapacity_thenReturnTooManyRequestsResponse()throwsInterruptedException{ ResponseEntityresponse=this.restTemplate.getForEntity(ADVANCED_GREETING,String.class); assertEquals(OK,response.getStatusCode()); for(inti=0;i<2;i++){ response=this.restTemplate.getForEntity(ADVANCED_GREETING,String.class); } assertEquals(TOO_MANY_REQUESTS,response.getStatusCode()); HttpHeadersheaders=response.getHeaders(); Stringkey="rate-limit-application_serviceAdvanced_127.0.0.1"; assertEquals("1",headers.getFirst(HEADER_LIMIT+key)); assertEquals("0",headers.getFirst(HEADER_REMAINING+key)); assertNotEquals("2000",headers.getFirst(HEADER_RESET+key)); TimeUnit.SECONDS.sleep(2); response=this.restTemplate.getForEntity(ADVANCED_GREETING,String.class); assertEquals(OK,response.getStatusCode()); }
在这里,我们快速连续两次调用,由于我们已将速率限制配置为每2秒一个请求,因此第二个调用将失败。结果,错误代码429(TooManyRequests)返回给客户端。以下是达到速率限制时返回的标头:
X-RateLimit-Limit-rate-limit-application_serviceAdvanced_127.0.0.1:1 X-RateLimit-Remaining-rate-limit-application_serviceAdvanced_127.0.0.1:0 X-RateLimit-Reset-rate-limit-application_serviceAdvanced_127.0.0.1:268
之后,我们休息了2秒钟。这是为端点配置的刷新间隔。最后,我们再次触发端点并获得成功的响应。
自定义密钥生成器
我们可以使用自定义密钥生成器自定义响应头中发送的密钥。这很有用,因为应用程序可能需要控制除type属性提供的选项之外的密钥策略。
例如,这可以通过创建自定义的RateLimitKeyGenerator实现类来完成。我们可以添加更多的限定符或完全不同的东西:
@Bean publicRateLimitKeyGeneratorrateLimitKeyGenerator(RateLimitPropertiesproperties, RateLimitUtilsrateLimitUtils){ returnnewDefaultRateLimitKeyGenerator(properties,rateLimitUtils){ @Override publicStringkey(HttpServletRequestrequest,Routeroute, RateLimitProperties.Policypolicy){ returnsuper.key(request,route,policy)+"_"+request.getMethod(); } }; }
上面的代码将REST方法名称附加到键。例如:
X-RateLimit-Limit-rate-limit-application_serviceSimple_127.0.0.1_GET:5
另一个关键点是 RateLimitKeyGeneratorbean将由spring-cloud-zuul-ratelimit自动配置。
自定义错误处理
该框架支持速率限制数据存储的各种实现。例如,提供了SpringDataJPA和Redis。默认情况下,使用DefaultRateLimiterErrorHandler 类将故障记录为错误。
当我们需要以不同方式处理错误时,我们可以定义一个自定义的RateLimiterErrorHandlerbean:
@Bean publicRateLimiterErrorHandlerrateLimitErrorHandler(){ returnnewDefaultRateLimiterErrorHandler(){ @Override publicvoidhandleSaveError(Stringkey,Exceptione){ //implementation } @Override publicvoidhandleFetchError(Stringkey,Exceptione){ //implementation } @Override publicvoidhandleError(Stringmsg,Exceptione){ //implementation } }; }
与RateLimitKeyGeneratorbean类似 ,也将自动配置RateLimiterErrorHandlerbean。
在GitHub上找到本文的完整代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。