Alibaba微服务组件 - Gateway(二) Spring Cloud Gateway快速开始

2.1 环境搭建

2.1.1 引入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

2.1.2 编写yml配置文件

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 网关gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一标识,路由到order
          uri: http://localhost:8020  # 需要转发的地址
          # 断言规则,用于路由规则的匹配
          predicates:
            # 因为是字符串所以可以这样写,自动映射
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add
            - Path=/order-serv/**
          # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
          filters:
            - StripPrefix=1 # 转发之前去掉1层路径(内置的一种过滤器) 变成http://localhost:8020/order/add

2.1.3 集成Nacos

引入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

编写yml配置文件

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 网关gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一标识,路由到order
          uri: lb://order-server  # 需要转发的地址  lb:使用nacos中本地的负载均衡策略 order-server 服务名
          # 断言规则,用于路由规则的匹配
          predicates:
            # 因为是字符串所以可以这样写,自动映射
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add
            - Path=/order-serv/**
          # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
          filters:
            - StripPrefix=1 # 转发之前去掉1层路径(内置的一种过滤器) 变成http://localhost:8020/order/add
    # 注册到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

简写: 去掉关于路由的配置,自动寻找服务

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 网关gateway配置
    gateway:
      discovery:
        locator:
          enabled: true # 是否启动我们的自动识别nacos服务  可以直接用服务名作为前缀访问,断言也是根据服务名做断言路由到我们的真实服务地址
                        # http://localhost:8088/order-server/order/add

    # 注册到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

测试(这时候,就发现只要按照网关地址/微服务/接口的格式去访问,就可以得到成功响应)
image

2.2 路由断言工厂(Route Predicate Factories)配置(局部,只针对某一个路由)

作用: 当请求gateway的时候,  使用断言对请求进行匹配, 如果匹配成功就路由转发, 如果匹配失败就返回404
类型:内置,自定义

2.2.1 内置断言工厂

官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

  • 基于Datetime类型的断言工厂
    此类型的断言根据时间做判断,主要有三个:
    AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
    BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
    BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内
    ZonedDateTime.now()
spring:
 cloud:
   gateway:
     routes:
     - id: after_route
       uri: https://example.org
       predicates:
       - After=2017-01-20T17:42:47.789-07:00[America/Denver]
  • 基于远程地址的断言工厂
    RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中
    ‐ RemoteAddr=192.168.1.1/24
  • 基于Cookie的断言工厂
    CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求
    cookie是否具有给定名称且值与正则表达式匹配。
    ‐ Cookie=chocolate, ch.
  • 基于Header的断言工厂
    HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。  判断请求Header是否具有给定名称且值与正则表达式匹配。
    ‐ Header=X‐Request‐Id, \d+
  • 基于Host的断言工厂
    HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
    ‐ Host=**.testhost.org
  • 基于Method请求方法的断言工厂
    MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
    ‐ Method=GET
  • 基于Path请求路径的断言工厂
    PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
    ‐ Path=/foo/{segment}
  • 基于Query请求参数的断言工厂
    QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。
    - Query=baz, ba.
  • 基于路由权重的断言工厂
    WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
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

2.2.2 自定义断言工厂

1. 必须spring组件 注入bean(@Component)
2. 类必须加上RoutePredicateFactory作为结尾
3. 必须继承AbstractRoutePredicateFactory
4. 必须声明静态内部类   声明属性来接收 配置文件中对应的断言的信息
5. 需要结合shortcutFieldOrder进行绑定
6. 通过apply进行逻辑判断  true就是匹配成功   false匹配失败

注意: 命名需要以 RoutePredicateFactory 结尾,必须注入到spring中

package com.xiexie.gateway.predicate;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * @Description 自定义断言工厂
 * @Date 2022-04-19 13:49
 * @Author xie
 */
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {

    public CheckAuthRoutePredicateFactory() {
        super(CheckAuthRoutePredicateFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        // 必须跟config中的属性名进行绑定
        return Arrays.asList("name");
    }

    @Override
    public Predicate<ServerWebExchange> apply(CheckAuthRoutePredicateFactory.Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                if (StringUtils.isEmpty(config.getName()) || !"xiexie".equals(config.getName())) {
                    return false;
                }
                return true;
            }

            @Override
            public String toString() {
                return String.format("CheckAuth: name=%s", config.getName());
            }
        };
    }

    /**
     * 用于接收配置文件中 断言的信息
     */
    @Validated
    public static class Config {

        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 网关gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一标识,路由到order
          uri: lb://order-server  # 需要转发的地址  lb:使用nacos中本地的负载均衡策略 order-server 服务名
          # 断言规则,用于路由规则的匹配
          predicates:
            # 因为是字符串所以可以这样写,自动映射
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add
            - Path=/order-serv/**
            # 自定义CheckAuth断言工厂
            - CheckAuth=xiexie
          # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
          filters:
            - StripPrefix=1 # 转发之前去掉1层路径(内置的一种过滤器) 变成http://localhost:8020/order/add
    # 注册到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

2.3 过滤器工厂( GatewayFilter Factories)配置(局部,只针对某一个路由)

Gateway 内置了很多的过滤器工厂,我们通过一些过滤器工厂可以进行一些业务逻辑处理器,比如添加剔除响应头,添加去除参数等
image

官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

2.3.1 内置过滤器工厂

过滤器工厂 作用 参数
AddRequestHeader 为原始请求添加Header Header的名称及值
AddRequestParameter 为原始请求添加请求参数 参数名称及值
AddResponseHeader 为原始响应添加Header Header的名称及值
DedupeResponseHeader 剔除响应头中重复的值 需要去重的Header名称及去重策略
Hystrix 为路由引入Hystrix的断路器保护 HystrixCommand的名称
FallbackHeaders 为fallbackUri的请求头中添加具体的异常信息 Header的名称
PrefixPath 为原始请求路径添加前缀 前缀路径
PreserveHostHeader 为请求添加一个preserveHostHeader=true 的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
RequestRateLimiter 用于对请求限流,限流算法为令牌桶 keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo 将原始请求重定向到指定的URL http状态码及重定向的url
RemoveHopByHopHeadersFilter 为原始请求删除IETF组织规定的一系列Header 默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader 为原始请求删除某个Header Header名称
RemoveResponseHeader 为原始响应删除某个Header Header名称
RewritePath 重写原始的请求路径 原始路径正则表达式以及重写后路径的正则表达式
RewriteResponseHeader 重写原始响应中的某个Header Header名称、值的正则表达式,重写后的值
SaveSession 在转发请求之前,强制执行WebSession::save操作
secureHeaders 为原始响应添加一系列起安全作用的响应头 无,支持修改这些安全响应头的值
SetPath 修改原始的请求路径 修改后的路径
SetResponseHeader 修改原始响应中某个Header的值 Header名称,修改后的值
SetStatus 修改原始响应的状态码 HTTP 状态码,可以是数字,也可以是字符串
StripPrefix 用于截断原始请求的路径 使用数字表示要截断的路径的数量
Retry 针对不同的响应进行重试 retries、statuses、methods、series
RequestSize 设置允许接收最大请求包的大 小。如果请求包大小超过设置的值,则返回 413 Payload Too Large 请求包大小,单位为字节,默认值为5M
ModifyRequestBody 在转发请求之前修改原始请求体内容 修改后的请求体内容
ModifyResponseBody 修改原始响应体的内容 修改后的响应体内容

2.3.1.1 添加请求头(上下文参考-全量配置文件)

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 网关gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一标识,路由到order
          uri: lb://order-server  # 需要转发的地址  lb:使用nacos中本地的负载均衡策略(loadbalancer) order-server 服务名
          # 断言规则,用于路由规则的匹配
          predicates:
            # 因为是字符串所以可以这样写,自动映射
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add   http://order-server/order-serv/order/add
            - Path=/order-serv/**
            # 自定义CheckAuth断言工厂
            - CheckAuth=xiexie # 数据放在自定义断言工厂的config中,根据config映射到的数据值进行判断是否匹配到
          # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
          filters:
            - StripPrefix=1 # 转发之前去掉1层路径(内置的一种过滤器) 变成http://localhost:8020/order/add
            # 其他的内置过滤器
            - AddRequestHeader=X-Request-color, red # 添加请求头参数
            # - AddRequestParameter=color, blue # 添加请求参数
            # - PrefixPath=/mall‐order # 添加前缀 对应微服务需要配置context‐path: /mall‐order
            # - RedirectTo=302, https://www.baidu.com/ # 重定向到百度
            # - CheckAuth=name, xiexie # 数据放在自定义的过滤器中,根据config映射到的数据进行判断是否放行
    # 注册到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

@RequestMapping("/header")
public String header(@RequestHeader("X-Request-color") String color) {
    System.out.println("gateWay获取请求头X‐Request‐color:" + color);
    return color;
}

2.3.1.2 添加请求参数(上下文参考上文全量配置文件)

- AddRequestParameter=color, blue # 添加请求参数
@RequestMapping("/parameter")
public String parameter(@RequestParam("color") String color) {
    System.out.println("gateWay获取参数color:" + color);
    return color;
}

2.3.1.3 为匹配的路由统一添加前缀(上下文参考上文全量配置文件)

- PrefixPath=/mall‐order # 添加前缀 对应微服务需要配置context‐path: /mall‐order
server:
  port: 8020
  servlet:
    context-path: /mall‐order

2.3.1.4 重定向操作(上下文参考上文全量配置文件)

- RedirectTo=302, https://www.baidu.com/ # 重定向到百度

.....

.....

还有很多,自行参照上面列表测试

2.3.2 自定义过滤器工厂

继承AbstractNameValueGatewayFilterFactory且我们的自定义名称必须要以GatewayFilterFactory结尾并交给spring管理

跟自定义的断言工厂相似

实现过滤工厂

package com.xiexie.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RedirectToGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * @Description
 * @Date 2022-04-19 15:37
 * @Author xie
 */
@Component
public class CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {

    public CheckAuthGatewayFilterFactory() {
        super(CheckAuthGatewayFilterFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("param", "value");
    }

    @Override
    public GatewayFilter apply(CheckAuthGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
                Set<String> stringSet = queryParams.keySet();
                if (stringSet.contains(config.getParam()) && config.getValue().equals(queryParams.get(config.getParam()).get(0))) {
                    // 正常请求
                    return chain.filter(exchange);
                } else {
                    // 返回404并结束
                    exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                    return exchange.getResponse().setComplete();
                }
            }
        };
    }

    public static class Config {

        private String param;

        private String value;

        public String getParam() {
            return param;
        }

        public void setParam(String param) {
            this.param = param;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }
}

配置文件中配置(上下文参考上文全量配置文件)

- CheckAuth=name, xiexie # 数据放在自定义的过滤器中,根据config映射到的数据进行判断是否放行

2.4 全局过滤器(Global Filters)配置

image

局部过滤器和全局过滤器区别:

  • 局部:局部针对某个路由, 需要在路由中进行配置
  • 全局:针对所有路由请求, 一旦定义就会投入使用
    GlobalFilter 接口和 GatewayFilter 有一样的接口定义,只不过, GlobalFilter 会作用于所有路由

2.4.1 自定义全局过滤器

package com.xiexie.gateway.filter.global;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @Description 自定义全局过滤器
 * @Date 2022-04-19 16:30
 * @Author xie
 */
@Component
public class GlobalLogFilter implements GlobalFilter {

    Logger log = LoggerFactory.getLogger(GlobalLogFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        log.info(exchange.getRequest().getPath().value());
        return chain.filter(exchange);
    }
}

2.4.2 Reactor Netty 访问日志(gateway自动定义的日志收集)

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#reactor-netty-access-logs
要启用 Reactor Netty 访问日志,请设置 -Dreactor.netty.http.server.accessLogEnabled=true

它必须是 Java 系统属性(设置JVM环境变量的),而不是 Spring Boot 属性。
java -jar xxxGatewat.jar -Dreactor.netty.http.server.accessLogEnabled=true

可以将日志记录系统配置为具有单独的访问日志文件。(比如配置在Logback)

2.5 Gateway跨域配置(CORS Configuration)

image

通过yml配置的方式
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#cors-configuration

2.5.1 通过yml配置的方式

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 网关gateway配置
    gateway:
      # 跨域配置
      globalcors:
        cors-configurations:
          '[/**]':  # 允许跨域访问的资源
            allowedOrigins: "*"  # 跨域允许的来源
            allowedMethods:  # 跨域允许的method
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION

2.5.1 通过java代码方式

package com.xiexie.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

/**
 * @Description 跨域配置
 * @Date 2022-04-19 17:06
 * @Author xie
 */
@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*"); // 允许跨域的method
        config.addAllowedOrigin("*"); // 允许跨域的来源
        config.addAllowedHeader("*"); // 允许跨域的header头

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config); // 允许跨域访问的资源
        return new CorsWebFilter(source);
    }
}

2.6 gateway整合sentinel流控降级

网关作为内部系统外的一层屏障, 对内起到一定的保护作用, 限流便是其中之一. 网关层的限流可以简单地针对不同路由进行限流, 也可针对业务的接口进行限流,或者根据接口的特征分组限流。
官方文档:https://github.com/alibaba/Sentinel/wiki/网关限流
image

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

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

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

Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑:

  • GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
  • ApiDefinition:用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫 my_api,请求 path 模式为 /foo/** 和 /baz/** 的都归到 my_api 这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。

2.6.1 添加依赖

<!--gateway 整合sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2.6.2 添加配置

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 整合sentinel
    sentinel:
      transport:
        # 添加sentinel控制台
        dashboard: 127.0.0.1:8858

image

具体操作可参考sentinel章节操作详解

2.6.3 自定义异常

2.6.3.1 通过配置完成

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 整合sentinel
    sentinel:
      transport:
        # 添加sentinel控制台
        dashboard: 127.0.0.1:8858
      # 自定义异常
      scg:
        fallback:
          mode: response
          response-body: '{"code":"403", "msg":"限流了"}'

2.6.3.2 通过GatewayCallbackManager(代码实现)

package com.xiexie.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description 自定义限流异常
 * @Date 2022-04-20 9:19
 * @Author xie
 */
@Configuration
public class GatewaySentinelConfig {

    @PostConstruct
    public void init() {

        BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {

            // 自定义异常处理
            Map<String, String> result = new HashMap<>();

            if (throwable instanceof FlowException) {
                // 限流

            } else if (throwable instanceof DegradeException) {
                // 降级

            } // .......

            result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
            result.put("code", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());

            return ServerResponse.status(HttpStatus.OK)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(result));
        };
    }
}

2.6.3 代码实现限流降级操作

用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则 GatewayConfiguration中添加:

package com.xiexie.gateway.config;

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.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @Description 自定义网关限流降级和异常
 * @Date 2022-04-20 9:19
 * @Author xie
 */
@Configuration
public class GatewaySentinelConfig {

    @PostConstruct
    public void init() {

        // 自定义异常信息
        initBlockRequestHandler();

        // 自定义网关限流规则
        initGatewayRules();
    }

    private void initBlockRequestHandler() {
        BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {

            // 自定义异常处理
            Map<String, String> result = new HashMap<>();

            if (throwable instanceof FlowException) {
                // 限流

            } else if (throwable instanceof DegradeException) {
                // 降级

            } // .......

            result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
            result.put("code", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());

            return ServerResponse.status(HttpStatus.OK)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(result));
        };
    }

    private void initGatewayRules() {

        Set<GatewayFlowRule> rules = new HashSet<>();
        //resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。
        //count:限流阈值
        //intervalSec:统计时间窗口,单位是秒,默认是 1 秒。
        rules.add(new GatewayFlowRule("order_route").setCount(2).setIntervalSec(1));
        rules.add(new GatewayFlowRule("user_server_api").setCount(2).setIntervalSec(1));

        // 加载网关规则
        GatewayRuleManager.loadRules(rules);
    }
}

其中网关限流规则 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 参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换成热点规则。
paramItem - parseStrategy 从请求中提取参数的策略,目前支持提取来源 IP(PARAM_PARSE_STRATEGY_CLIENT_IP)、Host(PARAM_PARSE_STRATEGY_HOST)、任意 Header(PARAM_PARSE_STRATEGY_HEADER)和任意 URL 参数(PARAM_PARSE_STRATEGY_URL_PARAM)四种模式。
paramItem - fieldName 若提取策略选择 Header 模式或 URL 参数模式,则需要指定对应的 header 名称或 URL 参数名称。
paramItem - pattern 参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控;若为空则统计该请求属性的所有值。(1.6.2 版本开始支持)
paramItem - matchStrategy 参数值的匹配策略,目前支持精确匹配(PARAM_MATCH_STRATEGY_EXACT)、子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)和正则匹配(PARAM_MATCH_STRATEGY_REGEX)。(1.6.2 版本开始支持)

用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则,或通过 GatewayRuleManager.register2Property(property) 注册动态规则源动态推送(推荐方式)

2.7 网关高可用

为了保证 Gateway 的高可用性,可以同时启动多个 Gateway 实例进行负载,在 Gateway 的上游使用 Nginx 或者 F5 进行负载转发以达到高可用。
image

image

posted @ 2022-04-19 18:00  xiexie0812  阅读(726)  评论(0编辑  收藏  举报