之前分享过 一篇 《Spring Cloud Gateway 原生的接口限流该怎么玩》, 核心是依赖Spring Cloud Gateway 默认提供的限流过滤器来实现
原生RequestRateLimiter 的不足
-
配置方式
spring: cloud: gateway: routes: - id: requestratelimiter_route uri: lb://pigx-upms order: 10000 predicates: - Path=/admin/** filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 3 key-resolver: "#{@remoteAddrKeyResolver}" #SPEL表达式去的对应的bean - StripPrefix=1 -
RequestRateLimiterGatewayFilterFactory
public GatewayFilter apply(Config config) { KeyResolver resolver = getOrDefault(config.keyResolver, defaultKeyResolver); RateLimiter<object> limiter = getOrDefault(config.rateLimiter, defaultRateLimiter); boolean denyEmpty = getOrDefault(config.denyEmptyKey, this.denyEmptyKey); HttpStatusHolder emptyKeyStatus = HttpStatusHolder .parse(getOrDefault(config.emptyKeyStatus, this.emptyKeyStatusCode));
1return (exchange, chain) -> { 2 return exchange.getResponse().setComplete(); 3 }); 4 }); 5};}
-
在实际生产过程中,必定不能满足我们的需求
生产中路由信息是保存数据库持久化或者配置中心,
RequestRateLimiterGatewayFilterFactory并不能随着持久化数据的改变而动态改变限流参数,不能做到实时根据流量来改变流量阈值
Sentinel Spring Cloud Gateway 流控支持
Sentinel 是什么?
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性,分布式系统的流量防卫兵。
从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流: route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
pom 依赖
1<!--Spring Cloud Alibaba 封装的 sentinel 模块--> 2<dependency> 3 <groupid>com.alibaba.cloud</groupid> 4 <artifactid>spring-cloud-alibaba-sentinel-gateway</artifactid> 5</dependency> 6 7<!--使用nacos 保存限流规则--> 8<dependency> 9 <groupid>com.alibaba.csp</groupid> 10 <artifactid>sentinel-datasource-nacos</artifactid> 11</dependency>
配置本地路由规则及其sentinel数据源
1spring: 2 application: 3 name: sentinel-spring-cloud-gateway 4 cloud: 5 gateway: 6 enabled: true 7 discovery: 8 locator: 9 lower-case-service-id: true 10 routes: 11 - id: pigx_route 12 uri: https://api.readhub.cn 13 predicates: 14 - Path=/topic/** 15 sentinel: 16 datasource.ds1.nacos: 17 server-addr: 127.0.0.1:8848 18 data-id: gw-flow 19 group-id: DEFAULT_GROUP 20 ruleType: gw-api-group 21 filter: 22 enabled: true
配置nacos数据源中的限流策略

-
常用限流策略 常量
以客户端IP作为限流因子 public static final int PARAM_PARSE_STRATEGY_CLIENT_IP = 0; 以客户端HOST作为限流因子 public static final int PARAM_PARSE_STRATEGY_HOST = 1; 以客户端HEADER参数作为限流因子 public static final int PARAM_PARSE_STRATEGY_HEADER = 2; 以客户端请求参数作为限流因子 public static final int PARAM_PARSE_STRATEGY_URL_PARAM = 3; 以客户端请求Cookie作为限流因子 public static final int PARAM_PARSE_STRATEGY_COOKIE = 4;
-
核心源码解析 SentinelGatewayFilter
sentinel通过扩展Gateway的过滤器,通过选择的不同GatewayParamParser 过处理请求限流因子和数据源中的配置进行比较 源码如下:
1public Mono<void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { 2 Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR); 3 4 Mono<void> asyncResult = chain.filter(exchange); 5 if (route != null) { 6 String routeId = route.getId(); 7 Object[] params = paramParser.parseParameterFor(routeId, exchange, 8 r -> r.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID); 9 String origin = Optional.ofNullable(GatewayCallbackManager.getRequestOriginParser()) 10 .map(f -> f.apply(exchange)) 11 .orElse(""); 12 asyncResult = asyncResult.transform( 13 new SentinelReactorTransformer<>(new EntryConfig(routeId, EntryType.IN, 14 1, params, new ContextConfig(contextName(routeId), origin))) 15 ); 16 } 17 18 Set<string> matchingApis = pickMatchingApiDefinitions(exchange); 19 for (String apiName : matchingApis) { 20 Object[] params = paramParser.parseParameterFor(apiName, exchange, 21 r -> r.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME); 22 asyncResult = asyncResult.transform( 23 new SentinelReactorTransformer<>(new EntryConfig(apiName, EntryType.IN, 1, params)) 24 ); 25 } 26 27 return asyncResult; 28} 29
效果演示
-
以上nacos 配置为 每秒只能通过5个请求,我们使用
jmeter 4.0来并发10个线程测试一下

-
通过上图可以结果证明sentinel限流确实有效
动态修改限流参数
sentinel-datasource-nacos作为sentinel的数据源,可以从如上 nacos 管理台实时刷新限流参数及其阈值- 目前sentinel dashboard 1.6.2 暂未实现gateway 流控图形化控制 ,
1.7.0会增加此功能
总结

欢迎关注我们获得更多的好玩JavaEE 实践