![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yNyAjY3ATO1QWMiJTM0I2N2EjM2IGZzQ2YzU2NiR2Mh9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
服务限流
服务网关的一个重要功能即服务限流。在Zuul中虽然可以通过前置类型过滤器对有效请求和无效请求(参数有效性,合法性)进行校验,将无效请求隔离在服务网关之外(返回错误提示信息、异常信息)。但有的时候某一个时间点内突发有效请求量很大,超过了系统可承受的范围,此时可以依靠服务限流算法,限制该时间点内通过的请求数,保证系统可用,避免出现服务雪崩效应。
此时可以通过Spring Cloud提供的Resilience4j限速器(RateLimiter)进行限速,也可以使用spring-cloud-zuul-ratelimit包。
项目依赖
<dependency> <groupId>io.github.resilience4jgroupId> <artifactId>resilience4j-spring-boot2artifactId> <version>0.13.2version> dependency> <dependency> <groupId>com.marcosbarbero.cloudgroupId> <artifactId>spring-cloud-zuul-ratelimitartifactId> <version>2.2.2.RELEASEversion> dependency>
使用Resilience4j限流
限流配置
#resilience4j限流配置resilience4j.ratelimiter.limiters.user-service.limit-for-period=3resilience4j.ratelimiter.limiters.user-service.limit-refresh-period-in-millis=5000resilience4j.ratelimiter.limiters.user-service.timeout-in-millis=10
上述配置中,resilience4j.ratelimiter.limiters.user-service.limit-for-period配置时间戳内的请求数(默认为50),limit-refresh-period-in-millis配置时间戳(单位毫秒,默认500),timeout-in-millis配置超时时间。user-service可以替换为其他有意义的名称。
限流过滤器
@Componentpublic class Resilience4jRateLimiterFilter extends ZuulFilter { //Resilience4j限速器注册机 @Autowired private RateLimiterRegistry rateLimiterRegistry; /** * 返回过滤器类型 * @return 前置类型过滤器 */ @Override public String filterType() { return FilterConstants.PRE_TYPE; } /** * 返回过滤器执行顺序 * @return */ @Override public int filterOrder() { return FilterConstants.PRE_DECORATION_FILTER_ORDER+5; } /** * 返回过滤器是否执行拦截操作 * @return 请求路径是否为/user/开头 */ @Override public boolean shouldFilter() { RequestContext requestContext=RequestContext.getCurrentContext(); String uri=requestContext.getRequest().getRequestURI(); return uri.startsWith("/user/"); } /** * 过滤器具体执行业务逻辑 * @return * @throws ZuulException */ @Override public Object run() throws ZuulException { //获取Resilience4j限速器(key与配置文件的Key名称一致) RateLimiter rateLimiter=rateLimiterRegistry.rateLimiter("user-service"); //限速器执行逻辑 Callable callResult=()->new ResultMessage(true,"执行成功"); //绑定限速器 Callable callResult2=RateLimiter.decorateCallable(rateLimiter,callResult); //尝试获取执行结果 Try resultMessageTry=Try.of(()->callResult2.call()).recover(ex->new ResultMessage(false,"超出限定流量,执行降级操作")); ResultMessage resultMessage=resultMessageTry.get(); //执行结果返回true,说明在限流范围内,限流操作成功,继续执行后续的自定义过滤器 if(resultMessage.isSuccess()) { return null; } //超过限流范围处理 RequestContext requestContext=RequestContext.getCurrentContext(); requestContext.setSendZuulResponse(false);//不在路由到下一个执行顺序的过滤器,直接返回失败 requestContext.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());//设置响应状态码(值为429) requestContext.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE);//设置响应类型(APPLICATION_JSON_UTF8_VALUE已废弃) ObjectMapper responseMapper=new ObjectMapper(); String responseBody=null; try { responseBody=responseMapper.writeValueAsString(resultMessage);//将执行结果转换为字符串并输出 } catch (Exception e) { e.printStackTrace(); } requestContext.setResponseBody(responseBody); return null; }}
在开发Zuul过滤器的过程中,需要熟悉Zuul过滤器类型及Zuul内置过滤器执行顺序。本例中,限流过滤器应该在过滤有效请求(请求参数有效)后执行,故过滤器执行顺序为前置(pre)过滤器PreDecorationFilter后路由(route)过滤器前(PreDecorationFilter用于处理请求上下文,SimpleHostRoutingFilter用于具体的URL请求转发)。
我们将Resilience4jRateLimiterFilter(Resilience4j限速过滤器)声明为组件(@Component),在入口程序中被扫描发现并装配;注入限速注册机(RateLimiterRegistry),通过限速注册机获取具体的Resilience4j限速器实例(rateLimiterRegistry.rateLimiter(key));通过RateLimiter.decorateCallable方法(参数为Resilience4j实例,限速器业务逻辑);在尝试获取执行结果的过程中发生异常(限流操作超出限制范围),通过recover方法实现服务降级相关操作(返回缓存结果、服务降级信息、异常),提供更好的用户体验;未超过服务限流范围执行后续的过滤器操作,超过服务限流范围则设置响应信息给服务调用方展现。
Spring-cloud-zuul-ratelimit限流
为了防止微服务系统中的服务调用接口被恶意频繁请求调用,需要在服务网关层进行服务限流对服务调用接口安全性进行保护。使用spring-cloud-zuul-ratelimit可以在Zuul的基础上实现服务网关限流相关功能。
限流方式
限流方式 | 限流描述 |
Authenticated User(认证用户) | 使用已经认证的用户名或'anonymous' |
Request Origin(原始请求) | 使用用户的原始请求 |
URL | 使用上游请求的地址 |
Global configuration per service(全局配置) | 针对每个服务的全局配置,无需指定限流方式 |
配置详解
#spring-cloud-zuul-ratelimit配置(全局配置)#是否开启限速配置(boolean,默认false)zuul.ratelimit.enabled=true #使用Redis缓存对应的度量数据zuul.ratelimit.repository=redis#是否开启代理服务(boolean,默认false)zuul.ratelimit.behind-proxy=true#是否添加响应报头(boolean,默认false)zuul.ratelimit.add-response-header=true#每个刷新时间窗口对应的请求数量限制zuul.ratelimit.default-policy.limit=10#每个刷新时间窗口对应的请求时间限制zuul.ratelimit.default-policy.quota=1000#刷新时间窗口时间(默认60,单位秒)zuul.ratelimit.default-policy.refresh-interval=60#全局限流方式(可选,user,origin,url)zuul.ratelimit.default-policy.type=user#spring-cloud-zuul-ratelimit配置(服务实例配置)#限制用户微服务实例(user-service)刷新时间窗口对应的请求数量限制zuul.ratelimit.user-service.limit=5#限制用户微服务实例(user-service)刷新时间窗口对应的请求时间限制zuul.ratelimit.user-service.quota=100#限制用户微服务实例(user-service)限流方式zuul.ratelimit.user-service.type=origin