SpringCloud 微服务网关 Gateway 组件

网关简介#

大家都都知道在微服务架构中,一个系统会被拆分为很多个微服务。那么作为客户端要如何去调用这么多的微服务呢?如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去用。

这样架构会存在很多问题:

  1. 每个业务都会需要鉴权、限流、权限校验、跨域等逻辑,如果每个业务都各自为战,自己造轮子实现一遍,会很蛋疼,完全可以抽出来,放到一个统一的地方去做。
  2. 如果业务量比较简单的话, 这种方式前期不会有什么问题,但随着业务越来越复杂,比如淘宝、亚马逊,打开一个页面可能会涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维护,涉及到数百个域名,另一方面是连接数的瓶颈,想象一 下你打开一个 APP,通过抓包发现涉及到了数百个远程调用,这在移动端下会显得非常低效。
  3. 后期如果需要对微服务进行重构的话, 也会变的非常麻烦,需要客户端配合你一起进行改造,比如商品服务,随着业务变的越来越复杂,后期需要进行拆分成多个微服务,这个时候对外提供的服务也需要拆分成多个,同时需要客户端配合你进行改造,非常蛋疼。

上面的问题可以借助 API 网关来解决。

注重稳定性:

  • 全局性流控
  • 日志统计
  • 防止 SQL 注入
  • 防止 Web 攻击
  • 屏蔽工具扫描
  • 黑白 IP 名单
  • 证书 / 加解密处理

提供更好的服务

  • 服务级别流控
  • 服务降级与熔断
  • 路由与负载均衡、灰度策略
  • 服务过滤、聚合与发现
  • 权限验证与用户等级策略
  • 业务规则与参数校验
  • 多级缓存策略

所谓的 API 网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一 些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等。添加上 API 网关之后,系统的架构图变成了如下所示:

SpringCloud 微服务网关 Gateway 组件

一、什么是 Spring Cloud Gateway#

网关作为流量的入口,常用的功能包括路由转发,权限校验,限流等。
Spring Cloud Gateway 是 Spring Cloud 官方推出的第二代网关框架,定位于取代 Netlix Zuul。相比 Zuul 来说,Spring Cloud Gateway 提供更优秀的性能,更强大的有功能。
Spring Cloud Gateway 由 WebFlux + Netty + Reactor 实现的响应式的 API 网关。它不能在传统的servlet 容器中工作,也不能构建成 war 包。
Spring Cloud Gateway 旨在为微服务架构提供一种简单且有效的 API 路由的管理方式,并基于 Filter 的方式提供网关的基本功能,例如说安全认证、监控、限流等等。

其它网关组件:
在 SpringCloud 微服务体系中,有个很重要的组件就是网关,在 1.x 版本中都是采用的 Zuul 网关;但在 2.x 版本中,zuul 的升级一直跳票,SpringCloud 最后自 己研发了一个网关替代 Zuul,那就是 SpringCloud Gateway
网上很多地方都说 Zuul 是阻塞的,Gateway 是非阻塞的,这么说是不严谨的,准确的讲 zuul 1.x 是阻塞的,而在 2.x 的版本中,Zuul 也是基于 Netty,也是非阻塞的,如果一定要说性能,其实没多大差距。

Spring Cloud Gateway 功能特征

  • 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0 进行构建;
  • 动态路由:能够匹配任何请求属性;
  • 支持路径重写;
  • 集成 Spring Cloud 服务发现功能 (Nacos、 Eruka);
  • 可集成流控降级功能 (Sentinel、 Hystrix);
  • 可以对路由指定易于编写的 Predicate (断言) 和 Filter (过滤器);

1.1、核心概念#

  • 路由(route)
    路由是网关中最基础的部分,路由信息包括一个 ID、一个目的 URI、一组断言工厂、一组 Filter 组成。如果断言为真,则说明请求的 URL 和配置的路由匹配。

  • 断言(predicates)
    Java8 中的断言函数,SpringCloud Gateway 中的断言函数类型是 Spring5.0 框架中的 ServerWebExchange。断言函数允许开发者去定义匹 Http request 中的任何信息,比如请求头和参数等。

  • 过滤器(Filter)
    SpringCloud Gateway 中的 filter 分为 Gateway Fller 和 Global Filter。Fiter 可以对请求和响应进行处理。

二、Spring Cloud Gateway 快速开始#

2.1、环境搭建#

2.1.1、引入依赖#

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

2.1.2、配置 application.yml 文件#

现有两个服务,order (订单) 和 stock (库存) 服务

SpringCloud 微服务网关 Gateway 组件

server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        # 路由唯一标识,路由到订单
        - id: order_route
          # 需要转发的地址
          uri: http://localhost:8020
          # 断言规则,用于路由规则的匹配
          predicates:
            - Path=/order-server/**
            # http://localhost:8088/order-server/order/add 路由到
            # http://localhost:8020/order-server/order/add
          filters:
            # 转发之前,去掉第一层的路径
            # http://localhost:8020/order/add
            - StripPrefix=1
        #- id: stock_route

启动网关服务,请求 http://localhost:8088/order-server/order/add 地址,可以看到路由到了订单服务,并且请求库存服务成功。

SpringCloud 微服务网关 Gateway 组件

2.2、集成 Nacos#

现在是在配置文件中写死了转发的路径地址

2.2.1、引入依赖#

<!--nacos 服务注册与发现-->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

2.2.2、编写配置文件#

server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.33.62:8847
        username: nacos
        password: nacos
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        # 路由唯一标识,路由到订单
        - id: order_route
          # lb 指的是从 nacos 中按照名称获取微服务,并遵循负载均衡策略
          uri: lb://order-server
          # 断言规则,用于路由规则的匹配
          predicates:
            - Path=/order-server/**
          filters:
            - StripPrefix=1

可以看到 Nacos 注册中心的服务

SpringCloud 微服务网关 Gateway 组件

此时请求接口 http://localhost:8088/order-server/order/add

SpringCloud 微服务网关 Gateway 组件

配置简写

server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.33.62:8847
        username: nacos
        password: nacos
    # gateway 配置
    gateway:
      discovery:
        locator:
          # 是否启动自动识别 nacos 服务
          enabled: true

需要通过 nacos 中的服务名进行访问 http://localhost:8088/order-server/order/add

三、Gateway 路由断言工厂(Route Predicate Factories)配置#

作用:当请求 gateway 时候,使用断言对请求进行匹配,如果匹配成功就路由转发,匹配失败就返回 404。

3.1、内置路由断言工厂#

官网地址

SpringCloud Gateway 包括许多内置的断言工厂,所有这些断言都与 HTTP 请求的不同属性匹配。具体如下:

3.1.1、基于 Datetime 类型的断言工厂#

此类型的断言,根据时间做判断,主要有三个:
AfterRoutePredicateFactory:接收一个日期参数,判断请求日期是否晚于指定日期
BeforeRoutePredicateFactory:接收一 个日期参数,判断请求日期是否早于指定日期
BetweenRoutePredicateFactory:接收两个日期参数,判断请求日期是否在指定时间段内

时间类型为 ZonedDateTime.now ()

- After=2022-08-12T15:00:00.000+08:00[Asia/Shanghai]
server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.33.62:8847
        username: nacos
        password: nacos
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        # 路由唯一标识,路由到订单
        - id: order_route
          # lb 指的是从 nacos 中按照名称获取微服务,并遵循负载均衡策略
          uri: lb://order-server
          # 断言规则,用于路由规则的匹配
          predicates:
            - Path=/order/**
            - After=2022-08-12T15:00:00.000+08:00[Asia/Shanghai]

请求成功

SpringCloud 微服务网关 Gateway 组件

修改时间为当前时间之前的时间后,访问接口为 404

SpringCloud 微服务网关 Gateway 组件

3.1.2、基于远程地址的断言工厂#

RemoteAddrRoutePredicateFactory:接收一个 IP 地址段,判断请求主机地址是否在地址段中。

- RemoteAddr=192.168.1.1/24

3.1.3、基于 Cookie 的断言工厂#

CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。判断请求 cookie 是否具有给定名称且值与正则表达式匹配

- Cookie=chocolate,ch.

3.1.4、基于 Header 的断言工厂#

HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。判断请求 Header 是否具有给定名称且值与正则表达式匹配。

设置请求头中,X-Request-Id,必须为数字

- Header=X-Request-Id,\d+

使用 postman 进行测试

SpringCloud 微服务网关 Gateway 组件

当请求头中没有 X-Request-Id 或者值不为数字时,访问 404

SpringCloud 微服务网关 Gateway 组件

3.1.5、基于 Host 的断言工厂#

HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的 Host 是否满足匹配规则。

- Host=**.testhost.org

3.1.6、基于 Method 请求方法的断言工厂#

MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。

- Method=GET

当请求为 POST 请求时,404

SpringCloud 微服务网关 Gateway 组件

3.1.7、基于 Path 请求路径的断言工厂#

PathRoutePredicateFactory:接收一个参数,判断请求的 URI 部分是否满足路径规则。

# 效果类似于 url 中 /foo/xxx
- Path=/foo/{segment}

3.1.8、基于 Query 请求参数的断言工厂#

QueryRoutePredicateFactory:接收两个参数,请求 param 和正则表达式,判断请求参数是否具有给定名称且值与正则表达式匹配。

# 效果类似于 url 中 ?baz=xxx
- Query=name,ba.

SpringCloud 微服务网关 Gateway 组件

3.1.9、基于路由权重的断言工厂#

WeightRoutePredicateFactory:接收一个 [组名权重],然后对于同一个组内的路由按照权重转发

routes:
  - id: weight_route1
    uri: host1
    predicates:
      - Path=/order/**
      - weitht=group3,1
  - id: weight_route2
    uri: host2
    predicates:
      - Path=/order/**
      - Weight=group3,9

3.2、自定义路由断言工厂#

自定义路由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply 方法的逻辑。在 apply 方法中可以通过 exchange.getRequest () 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息。

注意:类命名必须以 RoutePredicateFactory 结尾

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

实现效果为,当路由中有 hudu 字符串时,通过

@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {

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

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

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

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

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

application.yml 配置

server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.33.62:8847
        username: nacos
        password: nacos
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        # 路由唯一标识,路由到订单
        - id: order_route
          # lb 指的是从 nacos 中按照名称获取微服务,并遵循负载均衡策略
          uri: lb://order-server
          # 断言规则,用于路由规则的匹配
          predicates:
            - Path=/order/**
            - CheckAuth=hudu

当 CheckAuth 不为 hudu 时,请求 404

四、过滤器工厂(GatewayFilter Factories)配置#

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

4.1、内置过滤器#

官方文档地址

SpringCloud 微服务网关 Gateway 组件

SpringCloud 微服务网关 Gateway 组件

4.1.1、添加请求头#

spring:
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        # 路由唯一标识,路由到订单
        - id: order_route
          # lb 指的是从 nacos 中按照名称获取微服务,并遵循负载均衡策略
          uri: lb://order-server
          # 断言规则,用于路由规则的匹配
          predicates:
            - Path=/order/**
          filters:
            # 添加请求头
            - AddRequestHeader=X-Request-color,red
@RequestMapping("/header")
public String header(@RequestHeader("X-Request-color") String color) {
  return color;
}

SpringCloud 微服务网关 Gateway 组件

4.1.2、添加请求参数#

spring:
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        # 路由唯一标识,路由到订单
        - id: order_route
          # lb 指的是从 nacos 中按照名称获取微服务,并遵循负载均衡策略
          uri: lb://order-server
          # 断言规则,用于路由规则的匹配
          predicates:
            - Path=/order/**
          filters:
            # 添加请求参数
            - AddRequestParameter=color,blue
@GetMapping("/parameter")
public String parameter(@RequestParam("color")String color) {
  return color;
}

SpringCloud 微服务网关 Gateway 组件

4.1.3、为匹配路由统一添加前缀#

filters:
  # 添加请求参数
  - PrefixPath=/order-server

效果等同于在远处调用的服务上添加

server:
  servlet:
    context-path: order-server

请求接口 http://localhost:8088/order/test 实际是转发到 http://localhost:8088/order-server/order/test

4.1.4、重定向#

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

请求接口 http://localhost:8088/order/test

4.2、自定义过滤器工厂(局部过滤器)#

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

实现效果当请求接口中带有 ?name=hudu 时,才能请求成功

@Component
public class CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {


    private static final Log log = LogFactory
            .getLog(CheckAuthGatewayFilterFactory.class);

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

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

    @Override
    public GatewayFilter apply(CheckAuthGatewayFilterFactory.Config config) {
        return (exchange, chain) -> {
            // 获取 key value 值
            String key = exchange.getRequest().getQueryParams().getFirst(config.getKey());
            // 如果 key value 等于对应的值 成功
            if (StringUtils.hasText(key)) {
                if (config.getValue().equals(key)) {
                    return chain.filter(exchange);
                }
            }
            // 否则访问 404
            exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
            return exchange.getResponse().setComplete();
        };
    }

    public static class Config {

        private String key;

        private String value;

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public String getValue() {
            return value;
        }

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

}
filters:
  - CheckAuth=name,hudu

SpringCloud 微服务网关 Gateway 组件

4.3、全局过滤器(Global Filters)配置#

局部过滤器和全局过滤器的区别:
局部过滤器:只针对某一个路由进行过滤,需要在路由中进行配置
全局过滤器:针对所有路由请求,一旦定义了就会投入使用

SpringCloud 微服务网关 Gateway 组件

4.3.1、LoadBalancerClientFilter#

LoadBalancerClientFilter 会查看 exchange 的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值(一个 URI),如果该值的 scheme 是 lb,比如:lb://myserver,它会使用 SpringCloud 的 LoadBalancerClient 来将 myservice 解析成实际的 host 和 port,并替换掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的内容。
其实就是用来整合负载均衡 Ribbon 的

spring:
  cloud:
    gateway:
      routes:
        - id: order_route
          uri: lb://mall-order
          predicates:
            - Path=/order/**

4.3.2、自定义全局过滤器#

一般像授权,权限认证,日志记录等,会使用全局过滤器

@Component
public class MyGlobalFilter implements GlobalFilter {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info(exchange.getRequest().getPath().value());
        return chain.filter(exchange);
    }
}

如果既定义了局部过滤器,又定义了全局过滤器,会先经过局部过滤器,通过之后再经过全局过滤器

五、请求日志记录 & 跨域处理#

5.1、Reactor Netty 访问日志#

要启用 Reactor Netty 访问日志,需要设置 -Dreactor.netty.http.server.accessLogEnabled=true
它必须是 Java 系统属性,而不是 Spring Boot 属性。

可以将日志记录为具有单独的访问日志文件。
logback.xml

<appender name="accessLog" class="ch.qos.logback.core.FileAppender">
  <file>access_log.log</file>
    <encoder>
    <pattern>%msg%n</pattern>
  </encoder>
</appender>

<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
  <appender-ref ref="accessLog" />
</appender>

<logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
  <appender-ref ref="async"/>
</logger>

SpringCloud 微服务网关 Gateway 组件

SpringCloud 微服务网关 Gateway 组件

六、Gateway 跨域配置(CORS Configuration)#

通过 yml 配置方式

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          # 允许跨域访问的资源
          '[/**]':
            # 跨域允许来源
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTOIN

通过 javaconfig 配置方式

@Configuration
public class SystemCorsFilter {
    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 允许的 method
        corsConfiguration.addAllowedMethod("*");
        // 允许的来源
        corsConfiguration.addAllowedOrigin("*");
        // 允许的请求头
        corsConfiguration.addAllowedHeader("*");

        //由于是通过 webflux 的方式,需要额外添加此配置
        // 访问的资源
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);
    }
}

七、Gateway 整合 Sentinel 限流#

网关作为内部系统外的一层屏障,对内起到一定的保护作用,限流便是其中之一。网关层的限流可以简单地针对不同路由进行限流,也可针对业务的接口进行限流,或者根据接口的特征分组限流。

文档地址

  • 添加依赖
<!--sentinel 整合 gateway-->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!--sentinel 依赖-->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  • 添加配置
# 配置 sentinel
sentinel:
  transport:
    dashboard: 127.0.0.1:8858

7.1、控制台实现方式#

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

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

order-server 服务中提供如下服务

@RequestMapping("/hello")
public String hello() {
  System.out.println("下单成功!");
  return "Hello World ";
}

@RequestMapping("/flow")
  public String flow() {
  return "正常访问";
}

@RequestMapping("/flowThread")
public String flowThread() throws InterruptedException {
  TimeUnit.SECONDS.sleep(2);
  return "正常访问";
}

@RequestMapping("/get")
public String get() {
  return "查询订单";
}

@RequestMapping("/err")
public String err() {
  int i = 1 / 0;
  return "hello";
}

@RequestMapping("/get/{id}")
public String getById(@PathVariable("id")Integer id) {
  System.out.println("正常访问");
  return "正常访问";
}

启动服务,可以看到 sentinel 已经为当前的路由生成了一个资源,设置流控 QPS 为 2,当请求网关频繁时,网关被流控了。

SpringCloud 微服务网关 Gateway 组件

SpringCloud 微服务网关 Gateway 组件

SpringCloud 微服务网关 Gateway 组件

7.2、Gateway 整合 Sentinel 流控降级详细配置#

7.2.1、Burst size:突发请求额外允许数#

当一秒请求三次以上时,才会进行限流

SpringCloud 微服务网关 Gateway 组件

7.2.2、针对断言工厂进行限流#

SpringCloud 微服务网关 Gateway 组件

精确:与设定的值相等
子串:模糊匹配
正则:正则表达式匹配

SpringCloud 微服务网关 Gateway 组件

注意,localhost 访问无效,需要通过 127.0.0.1 访问才行

SpringCloud 微服务网关 Gateway 组件

针对请求头中的 X-Request-Id 的值进行限流

SpringCloud 微服务网关 Gateway 组件

SpringCloud 微服务网关 Gateway 组件

其它配置可以自行测试。

7.2.3、根据详细的地址分组流控#

SpringCloud 微服务网关 Gateway 组件

现在就可以根据 api 分组进行流控

SpringCloud 微服务网关 Gateway 组件

SpringCloud 微服务网关 Gateway 组件

SpringCloud 微服务网关 Gateway 组件

7.2.4、降级#

SpringCloud 微服务网关 Gateway 组件

7.3、自定义降级返回信息#

7.3.1、通过 yml#

spring:
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:8858
      scg:
        fallback:
          mode: response
          response-body: '{"code":403,"msg":"限流了"}'

SpringCloud 微服务网关 Gateway 组件

7.3.2、通过 GatewayCallbackManager#

@Configuration
public class GatewayConfig {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @PostConstruct
    public void init() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                // 打印异常
                log.error(throwable.getMessage());

                HashMap<String, String> map = new HashMap<>();
                map.put("code", HttpStatus.TOO_MANY_REQUESTS.toString());
                if (throwable instanceof FlowException) {
                    map.put("msg", "接口限流了");
                } else if (throwable instanceof DegradeException) {
                    map.put("msg", "服务降级了");
                } else if (throwable instanceof ParamFlowException) {
                    map.put("msg", "热点参数限流了");
                } else if (throwable instanceof SystemBlockException) {
                    map.put("msg", "触发系统保护规则了");
                } else if (throwable instanceof AuthorityException) {
                    map.put("msg", "授权规则不通过");
                }
                // 自定义异常处理
                return ServerResponse
                        // 响应状态码
                        .status(HttpStatus.OK)
                        // 响应类型
                        .contentType(MediaType.APPLICATION_JSON)
                        // 响应内容
                        .body(BodyInserters.fromValue(map));
            }
        };

        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

SpringCloud 微服务网关 Gateway 组件

7.4、代码实现方式(了解)#

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

@Configuration
public class GatewayConfig {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @PostConstruct
    public void doInit() {
        // 初始化自定义的 API
        initCustomizedApis();
        // 初始化网关限流规则
        initGatewayRules();
        // 自定义限流异常处理器
        initBlockRequestHandler();
    }

    private void initCustomizedApis() {
        HashSet<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api = new ApiDefinition("user_service_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/user/**").setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        definitions.add(api);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    private void initGatewayRules() {
        HashSet<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_service_api").setCount(2).setIntervalSec(1));
        // 加载网关规则
        GatewayRuleManager.loadRules(rules);
    }

    @PostConstruct
    public void initBlockRequestHandler() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                // 打印异常
                log.error(throwable.getMessage());

                HashMap<String, String> map = new HashMap<>();
                map.put("code", HttpStatus.TOO_MANY_REQUESTS.toString());
                if (throwable instanceof FlowException) {
                    map.put("msg", "接口限流了");
                } else if (throwable instanceof DegradeException) {
                    map.put("msg", "服务降级了");
                } else if (throwable instanceof ParamFlowException) {
                    map.put("msg", "热点参数限流了");
                } else if (throwable instanceof SystemBlockException) {
                    map.put("msg", "触发系统保护规则了");
                } else if (throwable instanceof AuthorityException) {
                    map.put("msg", "授权规则不通过");
                }
                // 自定义异常处理
                return ServerResponse
                        // 响应状态码
                        .status(HttpStatus.OK)
                        // 响应类型
                        .contentType(MediaType.APPLICATION_JSON)
                        // 响应内容
                        .body(BodyInserters.fromValue(map));
            }
        };

        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

八、Gateway 网关高可用#

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

SpringCloud 微服务网关 Gateway 组件

本作品采用《CC 协议》,转载必须注明作者和本文链接
未填写
文章
247
粉丝
19
喜欢
219
收藏
63
排名:723
访问:9993
私信
所有博文
社区赞助商