sentinel 从1.6.0 版本开始,提供了Spring Cloud Gateway Adapter 模块,支持两种资源维度的限流。
- Route 维度
- 自定义API维度,可以利用提供的API来定义API分组,然后针对这些分组维度进行限流。需要引入如下的依赖。
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId> <version>1.7.1</version> </dependency>
Route 维度限流
添加一个配置类 GatewayConfiguration
@Configuration public class GatewayConfigurtion{ private final List<ViewResolver> viewResolvers; private final ServerCodeConfigurer serverCodecConfigurer; public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolvers, ServerCodecConfigurer serverCodecConfigurer){ this.viewResolvers=viewResolvers.getIfAvailable(Collections::emptyList) this.serverCodecConfigurer = serverCodecConfigurer; } // 注入 SentinelGatewayFilter @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public GlobalFilter SentinelGatewayFilter(){ } //注入限流异常处理器 @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SentinelGatewayBlockExceptionHandler SentinelGatewayBlockExceptionHandler (){ return new SentinelGatewayBlockExceptionHandler (viewResolvers,serverCodecConfigurer); } @PostConstruct public void doInit(){ initGatewayRules(); } //初始化限流规则 private void iniGatewayRules(){ Set<GatewayFlowRule> rules= new HashSet<>(); GatewayFlowRule gatewayFlowRule=new GatewayFlowRule("nacos-gateway-provider").setCount(1).setIntervalSec(1); rules.add(gatewayFlowRule); GatewayRuleManager.loadRules(rules); } }
配置类的主要功能如下:
- 注入一个全局限流过滤器SentinelGatewayFilter.
- 注入限流异常处理器
- 初始化限流规则。在当前版本中,sentinel-spring-cloud-gateway-adapter 还只能支持手动配置。
其中,GatewayFlowRule 网关限流规则中提供了如下属性。
- resource: 资源名称,可以是网关中的route名称或者用户自定义的API分组名称。
- resourceMode: 资源模型,限流规则则是针对API Gateway的 route(RESOURCE_MODE_ROUTE_ID)还是用户在Sentinel 中定义的API分组(RESOURCE_MODE_CUSTOM_API_NAME),默认route.
- grade:限流指标维度,同限流规则的grade 字段。
- count:限流阈值。
- intervalSec: 统计时间窗口,单位是秒, 默认是1 秒。
- controlBehavior: 流量整形的控制效果,同限流规则的controlBehavior字段,目前支持快速失败和匀速排队两种模式,默认快速失败。
- burst: 应对突发请求时额外允许的请求数目。
- maxQueueingTimeoutMs:匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。
- paramItem: 参数限流配置。若不提供,则代表针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换热点规则。其中的字段如下。
parseStrategy: 从请求中提取参数的策略,目前支持提取来源IP(PARAM_PARSE_STRATEGY_CLIENT_IP)、Host(PARAM_PARSE_STRATEGY_HOST)、任意Header(PARAM_PARSE_STRATEGY_HEADER)和任意URL 参数(PARAM_PARSE_STRATEGY_URL_PARAM)四种模式。
fieldName:若提取策略选择Header模式或者URL参数模式,则需要指定对应的Header名称或URL参数名称。
pattern和matchStrategy: 为后续参数匹配特性预留,目前末实现。
网关限流规则的加载可以通过GatewayRuleManager.loadRules(rules); 的方式手动加载,也可以通过GatewayRuleManager.register2Property(property)注册动态限流规则(建议使用这种动态限流规则的方式)。
application.yml 文件中的配置如下,由于SentinelGatewayFilter是全局过滤器,网关配置不需要做任何调整。
spring: application: name: apring-cloud-nacos-gateway-consumer cloud: nacos: discovery: server-addr: 192.168.216.128:8848 gateway: discovery: locator: enabled: false#开启从注册中心动态创建路由的功能,利用微服务名进行路由 lower-Case-Service-id: false routes: - id: nacos-gateway-provider uri: lb://spring-cloud-nacos-gateway-provider predicates: - Path=/nacos/** filters: # 验证码处理 - StripPrefix=1
最后,通过测试工具访问http://localhsot:8888/nacos/say, 当触发限流之后,会获得如下内容。
Blocked by Sentinel: ParamFlowException
自定义API分组限流
自定义API分组限流实际上就是让多个Route公用一个限流规则。举例来说,假设有如下两个URI匹配规则。
spring: cloud: gateway: routes: id: foo_route uri: http://www.foo.com predicates: - Path=/foo/** - id: baz_route uri:http://www.baz.com predicates: -Path=/baz/**
如果我们希望这两个路由共用同一个限流规则,则可以采用自定义API分组限流的方式来实现。
private void initCustomizedApis(){ Set<ApiDefinition> definitions= new HashSet<>(); ApiDefinition apiDefinition=new ApiDefinition("first_customized_api") apiDefinition.setPredicateItem(new HashSet<ApiPredicateItem>(){{ add(new ApiPathPredicateItem().setPattern("/foo/**")); add(new ApiPathPredicateItem().setPattern("/baz/**").setMatchStrategy (SentinelGatewayContants.URL_MATCH_STRATEGY_PREFIX)); }}); definitions.add(apiDefinition); GatewayApiDefinitionManager.loadApiDefinitions(difinitions); }
上述代码主要是降/foo/** 和 /baz/**进行统一的分组,并提供一个name=first_sustomized_api, 然后再初始化网关限流规则时,针对该name设置限流规则。同时,我们可以通过setMatchStrategy 来设置不同path下的限流参数策略。
private void initGatewayRules(){ GatewayFlowRule customerFlowRule=new GatewayFlowRule("first_customized_api").
setResourceMode(SentinelGatewayContants.RESOURCE_MODE_CUSTOM_API_NAME).setCount(5).setIntervalSec(1); }
需要注意的是,再上述代码中,foo_route和 baz_route 这两个路由ID 与 first_customized_api 都会标记为Sentinel 的资源(限流资源标记)。比如,当访问网关的URI为http://localhsot:8888/foo/1时,Sentinel会统计foo_route、baz_route、first_customized_api 这些资源的流量情况。