一、简介
  • Spring Cloud Gateway 旨在提供一种简单而有效的方式来路由到 API,并为它们提供横切关注点,例如:安全性、监控/指标和弹性。
  • Spring Cloud GateWay 组件的核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发(路由)到对应的微服务。
  • Spring Cloud GateWay 的核心功能过滤和路由。

官方文档地址:https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/

中文文档(没有新版本):https://www.docs4dev.com/docs/zh/spring-cloud/Greenwich.RELEASE

Gateway 配置:https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/appendix.html

二、核心概念
  • Route :路由网关的基本构建块。它由 ID,目标 URI,谓词集合和 filte r集合定义。如果聚合谓词为 true,则匹配路由。

  • Predicate :Java 8 函数谓词。Importing 类型为 Spring Framework ServerWebExchange。这使开发人员可以匹配 HTTP 请求中的所有内容,例如 Headers 或参数。

  • filter :这些实例Spring Framework GatewayFilter 是使用特定工厂构造的。在此,可以在发送下游请求之前或之后修改请求和响应。

1、导入核心依赖
		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>2.2.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
            <version>2.2.7.RELEASE</version>
        </dependency>
2、Predicate 常用参数配置
spring:
  cloud:
    gateway:
      discovery: # 会从 nacos 注册中心查找服务
        locator: # 如:原来访问 http://127.0.0.1:21000/config/student 接口访问不通(没配置路由规则),加上服务名即可访问 http://127.0.0.1:21000/SPRING-CLOUD-SERVICE-CONFIG-PROVIDER/config/student
          enabled: true # 默认值 false,默认 predicates 路径 /serviceId/**,默认过滤器 RewritePath=/serviceId/(?<segment>.*), /serviceId/$\{segment}
          lower-case-service-id: false # 默认值 false,将 serviceId 转小写      
      routes:
        - id: service1 # 设置路由id(理论上是可以随便写的)
          order: 0 # order 越小,优先级越高
          uri: lb://SPRING-CLOUD-SERVICE-OPENFEIGN     # 设置路由的url  lb://service1(可以使用 nacos 服务注册名,lb 固定写法代表注册) http://localhost:3901(也可以使用绝对地址)
          predicates:
            - Method=GET,POST # 支持方法
            - Path=/service1/**,/service2/**  # 支持数组配置, 路径匹配规则,其他微服务 yaml 配置文件添加 server.servlet.context-path=/service 或 @RequestMapping("/service")
            - After=2022-05-05T02:00:00.000+08:00[Asia/Shanghai] # 路由指定时间后生效、还有其他的参数(Before、Between)
            # - Between=2022-05-05T02:00:00.000+08:00[Asia/Shanghai],2023-05-05T02:00:00.000+08:00[Asia/Shanghai]
            # - Before=2022-05-05T02:00:00.000+08:00[Asia/Shanghai]
            - Query=type # 请求包含 type 查询参数,则此路由将匹配。如 /hystrix/calculate?a=10&b=5&type=/ 则匹配成功
            # - Query=type,[\+\-\*/] # 请求包含 type 查询参数,并且值满足正则表达式,则此路由将匹配。如 /hystrix/calculate?a=10&b=5&type=/ 则匹配成功 /hystrix/calculate?a=10&b=5&type=% 则匹配失败
            # - RemoteAddr=192.168.3.1/24 # 如果请求的远程地址为192.168.3.240,则此路由将匹配。
            # - Cookie=chocolate, ch.p # 名为 chocolate 的 cookie,该 cookie 的值与ch.p正则表达式匹配。
            - Header=Content-Type, application/json;charset=UTF-8 # 请求头必须携带Content-Type,并且值为 application/json;charset=UTF-8 才生效,值支持正则表达式
            - Header=token, 123456789abcdefg # 请求头必须携带 token,并且值为 123456789abcdefg 路由才生效
            - Host=127.0.0.1:**,**.0.0.1:21000 # HTTP/1.1 请求头 Headers 默认包含 Host 头,可以是 IP,域名

postman 测试路由规则 GET 请求:http://127.0.0.1:21000/service1/hystrix/calculate?a=10&b=5&type=/
在这里插入图片描述
Predicate Weight 路由规则配置

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

该路由会将约 80% 的流量转发到 weighthigh.org,将约 20% 的流量转发到 weightlow.org

3、filter 配置
①、相关配置参数
spring:
  cloud:
    gateway:
      routes:
        - id: service1
          order: 0
          uri: lb://SPRING-CLOUD-SERVICE-OPENFEIGN
          predicates:
            - Method=GET,POST
            - Path=/service1/**,/service2/**  # 如果前端请求路径包含 api,则应用这条路由规则, 支持数组配置
            - After=2022-05-05T02:00:00.000+08:00[Asia/Shanghai] # 路由指定时间后生效、还有其他的参数(Before、Between)
          filters:
            # - RewritePath=/service1/(?<segment>.*), /$\{segment} # 重写路由地址,http://127.0.0.1:21000/service1/hystrix/calculate 会转发到 http://127.0.0.1:20004/hystrix/calculate,由于 YAML 规范,$ 被 $\ 取代。
            - RewritePath=/service1/(?<segment>.*), /service1/$\{segment}
            # - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
            - RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=123456 # 重写 response 请求头
            - StripPrefix=0 # 过滤器StripPrefix,作用是去掉请求路径的最前面n个部分截取掉。StripPrefix=1就代表截取路径的个数为1,比如前端过来请求/test/good/1/view,匹配成功后,路由到后端的请求路径就会变成http://localhost:8888/good/1/view
            - AddRequestHeader=Accept-Language, zh,zh-CN;q=0.9 # 将 Accept-Language=zh,zh-CN;q=0.9 添加到所有匹配请求的 header中
            - AddRequestParameter=host, 127.0.0.1 # 将 host=127.0.0.1 添加到所有匹配请求的 Param 参数中
            - AddResponseHeader=X-Response-Foo, Bar # 将X-Response-Foo:BarHeaders 添加到所有匹配请求的下游响应的 Headers 中。
            - PrefixPath=/mypath # 将/mypath作为所有匹配请求的路径的前缀。因此,对 /service1 的请求将发送到 /mypath/service1。
            - RedirectTo=302, https://blog.csdn.net/qq_41538097/article/details/124626658 # 所有匹配到的请求都重定向到该地址
            - RemoveRequestHeader=X-Request-Foo # 这将删除 Request Headers 中的 X-Request-Foo,然后将其发送到下游
            - RemoveResponseHeader=X-Response-Foo # 这将从响应中删除 Response Headers 中的 X-Response-Foo,然后将其返回给网关 Client 端。
            - RemoveRequestParameter=red # 将 red 参数发送到下游之前将其删除
            - SetPath=/{segment} # 请求路径/red/blue 设置为 /blue 发送到下游请求
            - SetRequestHeader=X-Request-Red, Blue # 注意:替换(而不是添加),替换 Request Header 的 X-Request-Red=Blue
            - SetResponseHeader=X-Response-Red, Blue # 注意:替换(而不是添加),替换 Response Header 的 X-Response-Red=Blue
            - SetStatus=401 # 无论哪种情况,响应的 HTTP 状态都设置为 401。也可以是枚举的字符串:NOT_FOUND
            - name: RequestRateLimiter # 令牌桶算法,IP 限流
		        args:
		          redis-rate-limiter.replenishRate: 10 # 每秒允许多少个请求
		          redis-rate-limiter.burstCapacity: 20 # 允许用户在一秒钟内执行的最大请求数,将此值设置为零将阻止所有请求。
            - name: Retry
                args:
                  retries: 3 # 请求失败重试 3 次
                  statuses: BAD_GATEWAY
                  methods: GET,POST
                  backoff:
                    firstBackoff: 10ms
                    maxBackoff: 50ms
                    factor: 2
                    basedOnPreviousValue: false
            - name: RequestSize # 限制请求大小,超过则拒绝,如果未配置,则默认请求大小设置为 5 MB。
              args:
                maxSize: 500MB # 单位默认B,支持 B、KB、MB、GB、TB
            - name: SetRequestHostHeader # 覆盖主机标头(HTTP/1.1 请求头 Headers 默认包含 Host 头,可以是 IP,域名
              args:
                host: 127.0.0.1
  

图片来源:https://blog.csdn.net/tuyong1972873004/article/details/107123254/
图片来源:https://blog.csdn.net/tuyong1972873004/article/details/107123254/

②、修改请求体、响应体

修改请求体和修改响应体只能使用代码配置,不能通过配置文件配置

@Configuration
public class RouteLocatorConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("code_service2_routeID", r -> r.method(HttpMethod.GET, HttpMethod.POST)
                        .and().path("/service1/openfeign/**")
                        .filters(f -> f
                                .modifyRequestBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE,
                                        (exchange, s) -> Mono.just("{\"body\":{\"message\": \"修改消息体\"}}"))
                                .modifyResponseBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE,
                                        (exchange, s) -> Mono.just("{\"body\":{\"message\": \"修改响应体\"}}"))
                        )
                        .uri("lb://SPRING-CLOUD-SERVICE-OPENFEIGN")
                 )
                .build();
    }
}

测试:http://127.0.0.1:21000/service1/openfeign/rule?type=1,可以看到响应体已修改成功
在这里插入图片描述

③、默认过滤器

要添加过滤器并将其应用于所有路由,您可以使用spring.cloud.gateway.default-filters. 属性参考上面的相关参数

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Red, Default-Blue
      - PrefixPath=/httpbin
4、Global Filters

应用于所有 route 的过滤器,优先级也可通过 @Order(-1) 指定,值越小优先级越高,filter 回调实现。
从 gateway 可以看到 GlobalFilter 有很多是实现类(SentinelGatewayFilter、RemoveCachedBodyFilter、LoadBalancerClientFilter 等),优先级最高是 RemoveCachedBodyFilter
在这里插入图片描述
自定义过滤器,可以获取传递的参数、header、响应 等做一些拦截处理

@Configuration
@Slf4j
public class DiyGlobalFilter {

    @Bean
    @Order(-1)
    public GlobalFilter customFilter() {
        return (exchange, chain) -> {
            String s = JSONObject.toJSONString(exchange.getRequest().getQueryParams());
            log.info("first pre filter, params:" + s);
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("first post filter,code:" + exchange.getResponse().getStatusCode());
            }));
        };
    }

    @Bean
    @Order(0)
    public GlobalFilter customFilter2() {
        return (exchange, chain) -> {
            log.info("second  pre filter");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("second post filter");
            }));
        };
    }

    @Bean
    @Order(1)
    public GlobalFilter customFilter3() {
        return (exchange, chain) -> {
            log.info("third pre filter");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("third post filter");
            }));
        };
    }
}

调用接口可以发现 filter 执行顺序,@Order(-1) pre -> @Order(0) pre -> @Order(1) pre -> @Order(1) post-> @Order(0) post -> @Order(-1) post
在这里插入图片描述

5、TLS/SSL

网关可以通过遵循常规的 Spring 服务器配置来侦听 https 上的请求。
https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/#tls-and-ssl

6、路由元数据配置

您可以使用元数据为每个路由配置附加参数

spring:
  cloud:
    gateway:
      routes:
      - id: route_with_metadata
        uri: https://example.org
        metadata:
          response-timeout: 200
          connect-timeout: 200

从 exchange 中获取所有元数据属性,可以从过滤器中获取

   @Bean
    @Order(-1)
    public GlobalFilter customFilter() {
        return (exchange, chain) -> {
        	// 获取元数据
            Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
            if (route != null) {
                Map<String, Object> metadata = route.getMetadata();
                Object responseTimeout= metadata.get("response-timeout");
                if (optionName != null) {
                    log.info(responseTimeout.toString());
                }
            }
            
            String s = JSONObject.toJSONString(exchange.getRequest().getQueryParams());
            log.info("first pre filter, params:" + s);
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("first post filter,code:" + exchange.getResponse().getStatusCode());
            }));
        };
    }

其中 ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR,可以直接写org.springframework.cloud.gateway.support.ServerWebExchangeUtils.gatewayRoute
在这里插入图片描述

7、Http超时配置

可以配置全局路由 Http 超时(响应和连接)或特定路由超时配置,特定会覆盖全局

还可以设置其他参数,org.springframework.cloud.gateway.config.HttpClientProperties ,和超时无关不详细说明
在这里插入图片描述
全局路由配置

spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000 # 单位毫秒
        response-timeout: 10s # 必须指定为 java.time.Duration

对于所有请求,连接超过 1s,响应超过 10 s 则报错

{
    "timestamp": "2022-05-12T08:03:08.225+00:00",
    "status": 500,
    "error": "Internal Server Error",
    "message": "",
    "path": "/service1/hystrix/calculate"
}

特定路由配置

spring:
  cloud:
    gateway:
      routes:
        - id: yaml_service1_routeID
          order: 0
          uri: lb://SPRING-CLOUD-SERVICE-OPENFEIGN
          metadata:
            response-timeout: 2000 # 单位毫秒
            connect-timeout: 200 # 单位毫秒
		  predicates:
            - Method=GET,POST
            - Path=/service1/hystrix/**

对于该路由规则,连接时间或响应时长超过 2s 则报如下错误

{
    "timestamp": "2022-05-12T08:22:18.051+00:00",
    "path": "/service1/hystrix2/calculate2",
    "status": 504,
    "error": "Gateway Timeout",
    "message": "",
    "requestId": "a3e65a4e-21"
}
8、DiscoveryClient 服务创建路由

使用服务注册中心注册的服务创建路由

spring:
  cloud:
    gateway:
      discovery: # 会从 nacos 注册中心查找服务
        locator: # 如:原来访问 http://127.0.0.1:21000/config/student 接口访问不通(没配置路由规则),加上服务名即可访问 http://127.0.0.1:21000/SPRING-CLOUD-SERVICE-CONFIG-PROVIDER/config/student
          enabled: true # 默认值 false,默认 predicates 路径 /serviceId/**,默认过滤器 RewritePath=/serviceId/(?<segment>.*), /serviceId/$\{segment}
          lower-case-service-id: false # 默认值 false,将 serviceId 转小写   
  • 从服务注册中心查找服务
  • 默认 predicates 路径 /serviceId/**
  • 默认过滤器 RewritePath=/serviceId/(?.*), /serviceId/${segment}

当然还可以配置其他 predicates 和 filters

spring.cloud.gateway.discovery.locator.predicates[0].name: 路径
spring.cloud.gateway.discovery.locator.predicates[0].args[pattern]: "'/'+serviceId+'/**'" 
spring.cloud.gateway.discovery.locator.predicates[1].name: 主机
spring.cloud.gateway.discovery.locator.predicates[1].args[pattern]: "'**.foo.com'" 
spring. cloud.gateway.discovery.locator.filters[0].name: Hystrix 
spring.cloud.gateway.discovery.locator.filters[0].args[name]: serviceId 
spring.cloud.gateway.discovery.locator.filters[1 ].name: RewritePath 
spring.cloud.gateway.discovery.locator.filters[1].args[regexp]: "'/' + serviceId + '/(?<remaining>.*)'" 
spring.cloud.gateway. discovery.locator.filters[1].args[replacement]: "'/${剩余}'"
9、CORS配置

允许来自 127.0.0.1:21000 所有 GET 请求的 CORS 请求。

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: 127.0.0.1:21000
            allowedMethods:
            - GET
10、其他配置
三、sentinel + gateway 网关限流

官方文档:https://github.com/alibaba/Sentinel/wiki/网关限流

从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:

  • route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
  • 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
1、sentinel 添加网关流控项目

Sentinel 1.6.3 引入了网关流控控制台的支持,用户可以直接在 Sentinel 控制台上查看 API Gateway 实时的 route 和自定义 API 分组监控,管理网关规则和 API 分组配置。

启动程序添加启动参数 -Dcsp.sentinel.app.type=1,idea 中 Edit Configurations 添加 vm 参数(顺便提一下,启动参数 -D 作用添加系统属性,使用 System.getProperty("csp.sentinel.app.type")即可获取到)
在这里插入图片描述
添加正确的启动参数并有访问量后,我们就可以在 Sentinel 上面看到对应的 API Gateway 了(假如项目第二次启动才添加 -Dcsp.sentinel.app.type=1,则 sentinel 不会将该项目当作网关流控项目,需要重启 sentinel 服务端)
在这里插入图片描述

2、route 维度
①、yaml 配置
spring:
  cloud:
    gateway:
      routes:
        - id: service1 # 设置路由id(理论上是可以随便写的)
          order: 0 # order 越小,优先级越高
          uri: lb://SPRING-CLOUD-SERVICE-OPENFEIGN     # 设置路由的url  lb://service1(可以使用 nacos 服务注册名,lb 固定写法代表注册) http://localhost:3901(也可以使用绝对地址)
          predicates:
            - Method=GET,POST # 支持方法
            - Path=/service1/**,/service2/**  # 支持数组配置, 路径匹配规则,其他微服务 yaml 配置文件添加 server.servlet.context-path=/service 或 @RequestMapping("/service")
            - After=2022-05-05T02:00:00.000+08:00[Asia/Shanghai] # 路由指定时间后生效、还有其他的参数(Before、Between)
            # - Between=2022-05-05T02:00:00.000+08:00[Asia/Shanghai],2023-05-05T02:00:00.000+08:00[Asia/Shanghai]
            # - Before=2022-05-05T02:00:00.000+08:00[Asia/Shanghai]
            - Query=type # 请求包含 type 查询参数,则此路由将匹配。如 /hystrix/calculate?a=10&b=5&type=/ 则匹配成功
            # - Query=type,[\+\-\*/] # 请求包含 type 查询参数,并且值满足正则表达式,则此路由将匹配。如 /hystrix/calculate?a=10&b=5&type=/ 则匹配成功 /hystrix/calculate?a=10&b=5&type=% 则匹配失败
            # - RemoteAddr=192.168.3.1/24 # 如果请求的远程地址为192.168.3.240,则此路由将匹配。
            # - Cookie=chocolate, ch.p # 名为 chocolate 的 cookie,该 cookie 的值与ch.p正则表达式匹配。
            - Header=Content-Type, application/json;charset=UTF-8 # 请求头必须携带Content-Type,并且值为 application/json;charset=UTF-8 才生效,值支持正则表达式
            - Header=token, 123456789abcdefg # 请求头必须携带 token,并且值为 123456789abcdefg 路由才生效
            - Host=127.0.0.1:**,**.0.0.1:21000 # HTTP/1.1 请求头 Headers 默认包含 Host 头,可以是 IP,域名
②、代码配置
@Configuration
public class RouteLocatorConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("code_service1", r -> r.method(HttpMethod.GET,HttpMethod.POST)
                        .and().path("/service1/**","/service2/**")
                        .and().after(ZonedDateTime.parse("2022-05-05T02:00:00.000+08:00[Asia/Shanghai]"))
                        .and().header(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8")
                        .and().query("type")
                        .and().header("token", "123456789abcdefg")
                        .and().host("127.0.0.1:**","**.0.0.1:21000")
                        .uri("lb://SPRING-CLOUD-SERVICE-OPENFEIGN")
                )
                .build();
    }
}
③、sentinel 控制台

在这里插入图片描述

④、添加 route 限流

在 sentinel 控制台添加 Route ID 网关限流,下面配置说明:如果每秒请求数 >= 1,则快速失败并限流 5 s
在这里插入图片描述

3、自定义 API 维度

支持代码配置或 sentinel 控制台配置

①、代码添加 API
@Configuration
public class GatewayConfiguration {

    @PostConstruct
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("code_service1_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/service1/sentinel"));
                    add(new ApiPathPredicateItem().setPattern("/service1/openfeign/**")
                            // 一共有三种,对应 sentinel 控制台匹配模式,不写默认精确
                            // URL_MATCH_STRATEGY_EXACT = 0;    精确
                            // URL_MATCH_STRATEGY_PREFIX = 1;   前缀
                            // URL_MATCH_STRATEGY_REGEX = 2;    正则
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        ApiDefinition api2 = new ApiDefinition("code_service2_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/service2/test"));
                }});
        definitions.add(api1);
        definitions.add(api2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }
}
②、sentinel 控制台添加 API 分组

在这里插入图片描述

③、sentinel 控制台

在这里插入图片描述

④、添加 API 限流

在 sentinel 控制台添加 API 分组网关限流,下面配置说明:如果每秒请求数 >= 1,则快速失败并限流 5 s
在这里插入图片描述

四、sentinel 网关限流处理逻辑
1、yaml 配置使用默认处理逻辑

spring-cloud-alibaba-sentinel-gateway 提供了如下配置选项,支持 yaml 配置限流处理逻辑

配置项含义默认值

spring.cloud.sentinel.scg.fallback.mode

Spring Cloud Gateway 流控处理逻辑 (选择 redirect or response)

spring.cloud.sentinel.scg.fallback.redirect

Spring Cloud Gateway 响应模式为 'redirect' 模式对应的重定向 URL

spring.cloud.sentinel.scg.fallback.response-body

Spring Cloud Gateway 响应模式为 'response' 模式对应的响应内容

spring.cloud.sentinel.scg.fallback.response-status

Spring Cloud Gateway 响应模式为 'response' 模式对应的响应码

429

spring.cloud.sentinel.scg.fallback.content-type

Spring Cloud Gateway 响应模式为 'response' 模式对应的 content-type

application/json

如果不配置限流逻辑,则返回如下结果(DefaultBlockRequestHandler.class)

{
    "code": 429,
    "message": "Blocked by Sentinel: ParamFlowException"
}
①、response 响应模式

reponse 返回错误数据

spring:
  cloud:
    sentinel:
      scg:
        fallback:
          content-type: application/json
          response-status: 429
          response-body: spring-cloud-gateway-study 服务限流
          mode: response

在这里插入图片描述

②、redirect 响应模式

redirect 重定向到新的页面

spring:
  cloud:
    sentinel:
      scg:
        fallback:
          redirect: https://blog.csdn.net/qq_41538097/article/details/124447411
          mode: redirect

在这里插入图片描述

2、自定义网关限流处理逻辑

GatewayCallbackManager 注册回调进行定制:

  • setBlockHandler:注册函数用于实现自定义的逻辑处理被限流的请求,对应接口为 BlockRequestHandler。
  • 默认实现为 DefaultBlockRequestHandler,当被限流时会返回类似于下面的错误信息:Blocked by Sentinel: FlowException。

新增 DiyBlockRequestHandler 类实现自定义网关限流处理逻辑

@Configuration
public class DiyBlockRequestHandler implements BlockRequestHandler {
    @Override
    public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable e) {
        String msg = null;
        if (e instanceof FlowException) {
            msg = "限流了,请稍后访问";
        } else if (e instanceof DegradeException) {
            msg = "降级了,返回默认数据";
        } else if (e instanceof ParamFlowException) {
            msg = "热点参数限流";
        } else if (e instanceof SystemBlockException) {
            msg = "系统规则(负载/...不满足要求)";
        } else if (e instanceof AuthorityException) {
            msg = "授权规则不通过";
        }

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("msg", msg);
        jsonObject.put("code",HttpStatus.TOO_MANY_REQUESTS);
        // 返回默认数据
        Map<Object, Object> map = new HashMap<>();
        map.put("username", "admin");
        map.put("address", "西安");
        jsonObject.put("mock", map);

        return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromObject(jsonObject));
    }

    @PostConstruct
    public void initBlockRequestHandler() {
        GatewayCallbackManager.setBlockHandler(this);
    }
}

结果如下
在这里插入图片描述

五、网关流控实现原理

当通过 GatewayRuleManager 加载网关流控规则(GatewayFlowRule)时,无论是否针对请求属性进行限流,Sentinel 底层都会将网关流控规则转化为热点参数规则(ParamFlowRule),存储在 GatewayRuleManager 中,与正常的热点参数规则相隔离。转换时 Sentinel 会根据请求属性配置,为网关流控规则设置参数索引(idx),并同步到生成的热点参数规则中。

外部请求进入 API Gateway 时会经过 Sentinel 实现的 filter,其中会依次进行 路由/API 分组匹配、请求属性解析和参数组装。Sentinel 会根据配置的网关流控规则来解析请求属性,并依照参数索引顺序组装参数数组,最终传入 SphU.entry(res, args) 中。Sentinel API Gateway Adapter Common 模块向 Slot Chain 中添加了一个 GatewayFlowSlot,专门用来做网关规则的检查。GatewayFlowSlot 会从 GatewayRuleManager 中提取生成的热点参数规则,根据传入的参数依次进行规则检查。若某条规则不针对请求属性,则会在参数最后一个位置置入预设的常量,达到普通流控的效果。

在这里插入图片描述

未完待续

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐