【云原生&微服务>SCG网关篇十二】Spring Cloud Gateway集成Sentinel API实现多种限流方式

网友投稿 543 2022-11-05


【云原生&微服务>SCG网关篇十二】Spring Cloud Gateway集成Sentinel API实现多种限流方式

文章目录

​​一、前言​​​​二、Gateway集成Sentinel API​​

​​0、集成Sentinel的核心概念​​

​​1)GatewayFlowRule 和 ApiDefinition​​​​2)GatewayFlowRule字段解释​​

​​1、针对Route维度限流​​

​​验证​​

​​2、针对API维度限流​​

​​验证​​

​​3、自定义限流异常返回值​​

​​验证​​

​​三、总结​​

一、前言

至此微服务网关系列文章已出:

​​【云原生&微服务>SCG网关篇一】为什么要有网关、生产环境如何选择网关​​​​云原生&微服务>SCG网关篇二】生产上那些灰度发布方式​​​​【云原生&微服务>SCG网关篇三】Spring Cloud Gateway是什么、详细使用案例​​​​云原生&微服务>SCG网关篇四】Spring Cloud Gateway内置的11种PredicateFactory如何使用​​​​【云原生&微服务>SCG网关篇五】Spring Cloud Gateway自定义PredicateFactory​​​​【云原生&微服务>SCG网关篇六】Spring Cloud Gateway内置的18种Filter使用姿势​​​​【云原生&微服务>SCG网关篇七】Spring Cloud Gateway基于内置Filter实现限流、熔断、重试​​​​【云原生&微服务>SCG网关篇八】Spring Cloud Gateway三种自定义Filter、GlobalFilter的方式​​​​【云原生&微服务>SCG网关篇九】Spring Cloud Gateway集成Nacos详细案例​​​​【云原生&微服务>SCG网关篇十】Spring Cloud Gateway集成Actuator、Zipkin详细案例​​​​【云原生&微服务>SCG网关篇十一】Spring Cloud Gateway解决跨域问题​​

聊了以下问题:

为什么要有网关?网关的作用是什么?网关的分类?网关的技术选型?使用网关时常用的灰度发布方式有哪些?Spring Cloud Gateway是什么?详细使用案例?Spring Cloud Gateway内置的11种PredicateFactory如何自定义PredicateFactory?Spring Cloud Gateway内置的18种常用的FilterSpring Cloud Gateway基于内置Filter实现限流、熔断、重试Spring Cloud Gateway三种自定义Filter、GlobalFilter的方式Spring Cloud Gateway集成Nacos案例Spring Cloud Gateway集成Actuator、Zipkin案例Spring Cloud Gareway如何解决CORS跨域问题

我们已经聊过了Spring Cloud Gateway的一种限流方式:使用内置的Filter(​​RequestRateLimiterGatewayFilterFactory​​)结合Redis使用令牌桶算法实现限流;这里我们再聊一下另外一种:Spring Cloud Gateway集成Sentinel实现限流。

PS:SpringCloud版本信息:

2.4.2 2020.0.1 2021.1 org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring-cloud-alibaba.version} pom import

二、Gateway集成Sentinel API

sentinel服务1.6.0以上的版本可支持整合到网关进行统一流控:

sentinel提供了两种资源维度的限流:

route维度:在配置文件中配置路由,资源名为对应的 routeId,一般是对某个微服务进行限流;这种维度属于粗粒度的限流。自定义API维度:通过Sentinel 提供的API来自定义一些API分组,针对某一类的uri进行匹配限流,可以跨多个微服务;这种维度属于细粒度的限流。

gateway整合sentinel默认不支持 URL 粒度限流;因此通过 Spring Cloud Alibaba 接入时,将​​spring.cloud.sentinel.filter.enabled​​ 配置为 false,以关闭流控控制台上的 URL 资源视图。

gateway整合Sentinel的maven依赖:

com.alibaba.csp sentinel-spring-cloud-gateway-adapter 1.8.0

0、集成Sentinel的核心概念

整体集成原理如下:

1)GatewayFlowRule 和 ApiDefinition

网关的限流规则GatewayFlowRule:

针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流;

用户自定义的API分组ApiDefinition:

ApiDefinition可以看做是一些 URL 匹配的组合;限流的时候可以针对这个自定义的 API 分组进行限流。

2)GatewayFlowRule字段解释

网关限流规则 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:参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控;若为空则统计该请求属性的所有值。(1.6.2 版本开始支持)matchStrategy:参数值的匹配策略,目前支持精确匹配(PARAM_MATCH_STRATEGY_EXACT)、子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)和正则匹配(PARAM_MATCH_STRATEGY_REGEX)。(1.6.2 版本开始支持)

可以通过GatewayRuleManager.loadRules(rules) 手动加载网关规则、 或 通过 GatewayRuleManager.register2Property(property) 注册规则(推荐方式);

特别注意:当使用 Spring Cloud Alibaba Sentinel 数据源模块时,需要注意网关流控规则数据源类型是 gw-flow,若将网关流控规则数据源指定为 flow 则不生效。

application.yml配置文件:

spring: application: name: nacos-gateway cloud: nacos: discovery: server-addr: 106.15.139.143:8848 gateway: discovery: locator: # 开启从注册中心动态创建路由的功能 enabled: true # 是否使用service-id的小写,默认是大写 lower-case-service-id: true routes: - id: gateway-nacos-service-route # 其中配置的lb://表示从注册中心获取服务,后面的gateway-nacos-provider表示目标服务在注册中心上的服务名 uri: lb://gateway-nacos-provider predicates: - Path=/nacos/sentinel/** #被限流 filters: - StripPrefix=2 # 自定义Sentinel API分组限流 - id: red_route uri: lb://gateway-nacos-provider predicates: - Path=/red/** #被限流 filters: - StripPrefix=1 - id: green_route uri: lb://gateway-nacos-provider predicates: - Path=/green/** #不被限流 filters: -

下面的三个样例均依赖此application.yml配置文件。

1、针对Route维度限流

搞一个ConfigurationClass,在类初始化的最后阶段(​​@PostConstruct​​​标注的方法)实例化一个​​GatewayFlowRule​​​,并将其添加到GatewayRuleManager中;​​GatewayFlowRule​​中指定针对哪个Route限流、限流的规则是什么?

package com.saint.gateway.sentinel;import java.util.Collections;import java.util.HashSet;import java.util.List;import java.util.Set;import javax.annotation.PostConstruct;import org.springframework.beans.factory.ObjectProvider;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.Ordered;import org.springframework.core.annotation.Order;import org.springframework.org.springframework.web.reactive.result.view.ViewResolver;import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;/** * 针对某个Route限流 * * @author Saint */@Configurationpublic class GatewayConfiguration { private final List viewResolvers; private final ServerCodecConfigurer serverCodecConfigurer; public GatewayConfiguration(ObjectProvider> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) { this.viewResolvers = viewResolvers.getIfAvailable(Collections::emptyList); this.serverCodecConfigurer = serverCodecConfigurer; } /** * 注入一个全局限流过滤器SentinelGatewayFilter * Filter的优先级最高 */ @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public GlobalFilter sentinelGatewayFilter() { return new SentinelGatewayFilter(); } /** * 注入限流异常处理器 * Filter的优先级最高 */ @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() { return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer); } /** * 初始化限流规则 */ @PostConstruct public void doInit() { initGatewayRules(); } /** * Route维度限流规则 */ private void initGatewayRules() { Set rules = new HashSet<>(); // 这里表示1s仅允许通过一个请求,GatewayFlowRule构造函数中的如参为路由名 GatewayFlowRule gatewayFlowRule = new GatewayFlowRule("gateway-nacos-service-route").setCount(1).setIntervalSec(1); rules.add(gatewayFlowRule); GatewayRuleManager.loadRules(rules); }}

当前案例表示针对routeId为​​gateway-nacos-service-route​​的路由做限流:1s仅允许一个请求通过;

验证

当请求被限流时会返回429状态码,响应体内容为:

{"code":429,"message":"Blocked by Sentinel: ParamFlowException"}

2、针对API维度限流

自定义API分组限流,将​​/nacos/sentinel/**​​​和​​/red/**​​进行统一分组,并提供name=saint_customized_api,然后在初始化网关限流规则时,针对该name设置限流规则;

package com.saint.gateway.sentinel;import java.util.Collections;import java.util.HashSet;import java.util.List;import java.util.Set;import javax.annotation.PostConstruct;import org.springframework.beans.factory.ObjectProvider;import org.springframework.cloud.gateway.filter.GlobalFilter;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.Ordered;import org.springframework.core.annotation.Order;import org.springframework.org.springframework.web.reactive.result.view.ViewResolver;import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;/** * 自定义API分组限流 * * @author Saint */@Configurationpublic class GatewayConfiguration1 { private final List viewResolvers; private final ServerCodecConfigurer serverCodecConfigurer; public GatewayConfiguration1(ObjectProvider> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) { this.viewResolvers = viewResolvers.getIfAvailable(Collections::emptyList); this.serverCodecConfigurer = serverCodecConfigurer; } /** * 注入SentinelGatewayFilter * Filter的优先级最高 */ @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public GlobalFilter sentinelGatewayFilter() { return new SentinelGatewayFilter(); } /** * 注入限流异常处理器 * Filter的优先级最高 */ @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() { return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer); } /** * 初始化限流规则 */ @PostConstruct public void doInit() { initCustomizedApis(); initGatewayRules(); } /** * 自定义API分组限流,将/nacos/sentinel/**和/red/**进行统一分组,并提供name=saint_customized_api,然后在初始化网关限流规则时,针对该name设置 * 限流规则。同时,可以通过setMatchStrategy来设置不同path下的限流参数策略 */ private void initCustomizedApis() { Set definitions = new HashSet<>(); // 自定义针对API限流的ApiDefinition,apiName可以随便取,在GatewayFlowRule中填入即可。 ApiDefinition apiDefinition = new ApiDefinition("saint_customized_api"); // 匹配下面请求路径的请求将被限流 apiDefinition.setPredicateItems(new HashSet() { { add(new ApiPathPredicateItem().setPattern("/nacos/sentinel/**") .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); add(new ApiPathPredicateItem().setPattern("/red/**") .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); } }); definitions.add(apiDefinition); GatewayApiDefinitionManager.loadApiDefinitions(definitions); } /** * 针对分组name来设置限流规则 */ private void initGatewayRules() { // 针对ApiDefinition进行限流 GatewayFlowRule rule = new GatewayFlowRule("saint_customized_api") .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME).setCount(1) .setIntervalSec(1); Set rules = new HashSet<>(); rules.add(rule); GatewayRuleManager.loadRules(rules); }}

验证

当我们访问​​/nacos/sentinel/**​​​和​​/red/**​​路径时才会被限流,其他路径均不会。

3、自定义限流异常返回值

Sentinel默认的异常处理器是​​com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler​​,我们可以根据这个Handler自定义一个WebExceptionHandler实现类。

1> 自定义WebExceptionHandler实现类:

package com.saint.gateway.sentinel;import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;import com.alibaba.csp.sentinel.slots.block.BlockException;import com.alibaba.csp.sentinel.util.function.Supplier;import org.springframework.core.io.buffer.DataBuffer;import org.springframework.org.springframework.org.springframework.org.springframework.web.reactive.function.server.ServerResponse;import org.springframework.web.reactive.result.view.ViewResolver;import org.springframework.web.server.ServerWebExchange;import org.springframework.web.server.WebExceptionHandler;import reactor.core.publisher.Mono;import java.util.List;/** * 异常处理器 */public class MySentinelGatewayBlockExceptionHandler implements WebExceptionHandler { private List viewResolvers; private List> messageWriters; private final Supplier contextSupplier = () -> { return new ServerResponse.Context() { public List> messageWriters() { return MySentinelGatewayBlockExceptionHandler.this.messageWriters; } public List viewResolvers() { return MySentinelGatewayBlockExceptionHandler.this.viewResolvers; } }; }; public MySentinelGatewayBlockExceptionHandler(List viewResolvers, ServerCodecConfigurer serverCodecConfigurer) { this.viewResolvers = viewResolvers; this.messageWriters = serverCodecConfigurer.getWriters(); } /** * 该方法的作用是,将限流的异常信息写回客户端 */ private Mono writeResponse(ServerResponse response, ServerWebExchange exchange) { ServerHttpResponse serverHttpResponse = exchange.getResponse(); serverHttpResponse.getHeaders().add("Content-type", "application/json;charset=UTF-8"); byte[] datas = "{\"code\":999,\"msg\":\"访问人数太多了,让我歇歇吧\"}".getBytes(); DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas); return serverHttpResponse.writeWith(Mono.just(buffer)); } public Mono handle(ServerWebExchange exchange, Throwable ex) { if (exchange.getResponse().isCommitted()) { return Mono.error(ex); } else { return !BlockException .isBlockException(ex) ? Mono.error(ex) : this.handleBlockedRequest(exchange, ex).flatMap((response) -> this.writeResponse(response, exchange)); } } private Mono handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) { return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); }}

2> 将MySentinelGatewayBlockExceptionHandler注入到Spring容器中:

// 注入自定义的限流异常处理器@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public MySentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() { return new MySentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);}

验证

接着根据API分组限流的案例进行验证;

响应的状态码是200,body是我们自定义的body:

{"code":999,"msg":"访问人数太多了,让我歇歇吧"}

三、总结

sentinel提供了两种资源维度的限流:

route维度:在配置文件中配置路由,资源名为对应的 routeId,一般是对某个微服务进行限流;这种维度属于粗粒度的限流。自定义API维度:通过Sentinel 提供的API来自定义一些API分组,针对某一类的uri进行匹配限流,可以跨多个微服务;这种维度属于细粒度的限流。

本文的演示案例基于文章()

PS:Sentinel整合Nacos配置源:

spring: cloud: #配置SpringCloudGateway的路由 sentinel: transport: dashboard: ip:port #sentinel控制台的请求地址 datasource: ds1: nacos: server-addr: ip:port data-id: ${spring.application.name}-${spring.profiles.active}-sentinel-gw-flow group-id: DEFAULT_GROUP data-type: json # 流控规则 rule-type: gw-flow ds2: nacos: server-addr: ip:port data-id: ${spring.application.name}-${spring.profiles.active}-sentinel-gw-api-group group-id: DEFAULT_GROUP data-type: json # api类型 rule-type: gw-api-group eager: true #立即加载 log: # 日志存放目录 dir:

引入maven依赖:

com.alibaba.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.cloud spring-cloud-alibaba-sentinel-gateway


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:韵达快递单号查询单号API(韵达快递单号查询单号查)
下一篇:Spring Initializr中生成的mvnw有什么用
相关文章

 发表评论

暂时没有评论,来抢沙发吧~