可恶,又被面试官装到了:Spring Cloud Gateway 网关限流怎么做?
一、背景
在我们平时开发过程中,一般一个请求都是需要经过多个微服务的,比如:请求从A服务流过B服务,如果A服务请求过快,导致B服务响应慢,那么必然会导致系统出现问题。因为,我们就需要有限流操作。
二、实现功能
提供自定义的限流key生成,需要实现KeyResolver接口。
提供默认的限流算法,实现实现RateLimiter接口。
当限流的key为空时,直接不限流,放行,由参数spring.cloud.gateway.routes[x].filters[x].args[x].deny-empty-key 来控制
限流时返回客户端的相应码有 spring.cloud.gateway.routes[x].filters[x].args[x].status-code 来控制,需要写这个 org.springframework.http.HttpStatus类的枚举值。
RequestRateLimiter 只能使用name || args这种方式来配置,不能使用简写的方式来配置。

RequestRateLimiter过滤器的redis-rate-limiter参数是在RedisRateLimiter的CONFIGURATION_PROPERTY_NAME属性配置的。构造方法中用到了。
三、网关层限流
限流的key 生成规则,默认是 PrincipalNameKeyResolver来实现 限流算法,默认是 RedisRateLimiter来实现,是令牌桶算法。
1、使用默认的redis来限流
在Spring Cloud Gateway中默认提供了 RequestRateLimiter 过滤器来实现限流操作。
1.引入jar包
<dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis-reactive</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies>
2.编写配置文件
spring:application:name: gateway-9205cloud:nacos:discovery:: localhost:8847gateway:routes:id: user-provider-9206uri: lb://user-provider-9206predicates:Path=/user/**filters:RewritePath=/user(?<segment>/?.*), $\{segment}name: RequestRateLimiterargs:# 如果返回的key是空的话,则不进行限流: false# 每秒产生多少个令牌: 1# 1秒内最大的令牌,即在1s内可以允许的突发流程,设置为0,表示阻止所有的请求: 1# 每次请求申请几个令牌: 1redis:host: 192.168.7.1database: 12port: 6379password: 123456server:port: 9205debug: true

3.网关正常响应

4.网关限流响应

2、自定义限流算法和限流key
1.自定义限流key
编写一个类实现 KeyResolver 接口即可。
package com.huan.study.gateway;import lombok.extern.slf4j.Slf4j;import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;import org.springframework.cloud.gateway.route.Route;import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;import org.springframework.http.server.reactive.ServerHttpRequest;import org.springframework.stereotype.Component;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;import java.util.Optional;/*** 限流的key获取** @author huan.fu 2021/9/7 - 上午10:25*/@Slf4j@Componentpublic class DefaultGatewayKeyResolver implements KeyResolver {@Overridepublic Mono<String> resolve(ServerWebExchange exchange) {// 获取当前路由Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);ServerHttpRequest request = exchange.getRequest();String uri = request.getURI().getPath();log.info("当前返回的uri:[{}]", uri);return Mono.just(Optional.ofNullable(route).map(Route::getId).orElse("") + "/" + uri);}}
配置文件中的写法(部分)
spring:cloud:gateway:routes:id: user-provider-9206filters:name: RequestRateLimiterargs:# 返回限流的key: "#{@defaultGatewayKeyResolver}"
2.自定义限流算法
编写一个类实现 RateLimiter ,此处使用内存限流
package com.huan.study.gateway;import com.google.common.collect.Maps;import com.google.common.util.concurrent.RateLimiter;import lombok.Getter;import lombok.Setter;import lombok.ToString;import lombok.extern.slf4j.Slf4j;import org.springframework.cloud.gateway.filter.ratelimit.AbstractRateLimiter;import org.springframework.cloud.gateway.support.ConfigurationService;import org.springframework.context.annotation.Primary;import org.springframework.stereotype.Component;import reactor.core.publisher.Mono;/*** @author huan.fu 2021/9/7 - 上午10:36*/4jpublic class DefaultGatewayRateLimiter extends AbstractRateLimiter<DefaultGatewayRateLimiter.Config> {/*** 和配置文件中的配置属性相对应*/private static final String CONFIGURATION_PROPERTY_NAME = "default-gateway-rate-limiter";private RateLimiter rateLimiter = RateLimiter.create(1);protected DefaultGatewayRateLimiter(ConfigurationService configurationService) {super(DefaultGatewayRateLimiter.Config.class, CONFIGURATION_PROPERTY_NAME, configurationService);}public Mono<Response> isAllowed(String routeId, String id) {log.info("网关默认的限流 routeId:[{}],id:[{}]", routeId, id);Config config = getConfig().get(routeId);return Mono.fromSupplier(() -> {boolean acquire = rateLimiter.tryAcquire(config.requestedTokens);if (acquire) {return new Response(true, Maps.newHashMap());} else {return new Response(false, Maps.newHashMap());}});}public static class Config {/*** 每次请求多少个 token*/private Integer requestedTokens;}}
配置文件中的写法(部分)
spring:cloud:gateway:routes:id: user-provider-9206filters:name: RequestRateLimiterargs:# 自定义限流规则: "#{@defaultGatewayRateLimiter}"
注意⚠️: 这个类需要加上 @Primary 注解。
3.配置文件中的写法
spring:application:name: gateway-9205cloud:nacos:discovery:: localhost:8847gateway:routes:id: user-provider-9206uri: lb://user-provider-9206predicates:Path=/user/**filters:RewritePath=/user(?<segment>/?.*), $\{segment}name: RequestRateLimiterargs:# 自定义限流规则: "#{@defaultGatewayRateLimiter}"# 返回限流的key: "#{@defaultGatewayKeyResolver}"# 如果返回的key是空的话,则不进行限流: false# 限流后向客户端返回的响应码429,请求太多: TOO_MANY_REQUESTS# 每次请求申请几个令牌 default-gateway-rate-limiter 的值是在 defaultGatewayRateLimiter 中定义的。: 1server:port: 9205debug: true
作者:huan1993
链接:https://juejin.cn/post/7005060165892309022
来源:掘金
