一、背景
在咱们平时开发过程中,个别一个申请都是须要通过多个微服务的,比方:申请从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包
<code class="xml"><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、编写配置文件
<code class="yaml">spring: application: name: gateway-9205 cloud: nacos: discovery: server-addr: localhost:8847 gateway: routes: - id: user-provider-9206 uri: lb://user-provider-9206 predicates: - Path=/user/** filters: - RewritePath=/user(?<segment>/?.*), $\{segment} - name: RequestRateLimiter args: # 如果返回的key是空的话,则不进行限流 deny-empty-key: false # 每秒产生多少个令牌 redis-rate-limiter.replenishRate: 1 # 1秒内最大的令牌,即在1s内能够容许的突发流程,设置为0,示意阻止所有的申请 redis-rate-limiter.burstCapacity: 1 # 每次申请申请几个令牌 redis-rate-limiter.requestedTokens: 1 redis: host: 192.168.7.1 database: 12 port: 6379 password: 123456 server: port: 9205 debug: true
3、网关失常响应
4、网关限流响应
2、自定义限流算法和限流key
1、自定义限流key
编写一个类实现 KeyResolver 接口即可。
<code class="java">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 @Component public class DefaultGatewayKeyResolver implements KeyResolver { @Override public 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); } }
配置文件中的写法(局部)
<code class="yaml">spring: cloud: gateway: routes: - id: user-provider-9206 filters: - name: RequestRateLimiter args: # 返回限流的key key-resolver: "#{@defaultGatewayKeyResolver}"
2、自定义限流算法
编写一个类实现 RateLimiter ,此处应用内存限流
<code class="java">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 */ @Component @Slf4j @Primary public 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); } @Override 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()); } }); } @Getter @Setter @ToString public static class Config { /** * 每次申请多少个 token */ private Integer requestedTokens; } }
配置文件中的写法(局部)
<code class="yaml">spring: cloud: gateway: routes: - id: user-provider-9206 filters: - name: RequestRateLimiter args: # 自定义限流规定 rate-limiter: "#{@defaultGatewayRateLimiter}"
留神⚠️:
这个类须要加上 @Primary
注解。
3、配置文件中的写法
<code class="yaml">spring: application: name: gateway-9205 cloud: nacos: discovery: server-addr: localhost:8847 gateway: routes: - id: user-provider-9206 uri: lb://user-provider-9206 predicates: - Path=/user/** filters: - RewritePath=/user(?<segment>/?.*), $\{segment} - name: RequestRateLimiter args: # 自定义限流规定 rate-limiter: "#{@defaultGatewayRateLimiter}" # 返回限流的key key-resolver: "#{@defaultGatewayKeyResolver}" # 如果返回的key是空的话,则不进行限流 deny-empty-key: false # 限流后向客户端返回的响应码429,申请太多 status-code: TOO_MANY_REQUESTS # 每次申请申请几个令牌 default-gateway-rate-limiter 的值是在 defaultGatewayRateLimiter 中定义的。 default-gateway-rate-limiter.requestedTokens: 1 server: port: 9205 debug: true
四、实现代码
https://gitee.com/huan1993/spring-cloud-alibaba-parent/tree/master/gateway-redis-limiter
五、参考文档
1、https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-redis-ratelimiter