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 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
247
粉丝
18
喜欢
217
收藏
62
排名:731
访问:9753
私信
所有博文
社区赞助商