文章目录

  • 1、什么是Spring Cloud Gateway
  • 1.2、Spring Cloud Gateway功能特征
  • 1.3、核心概念
  • 2、Gateway初体验
  • 2.1、Gateway快速开始
  • 3、Gateway整合nacos
  • 4、内置断言工厂
  • 4.1、基于Datetime类型
  • 4.2、基于远程地址
  • 4.3、基于Cookie
  • 4.4、基于Header
  • 4.5、基于Host
  • 4.6、基于Method请求方法
  • 4.7、基于Path匹配请求路径
  • 4.8、基于weight权重
  • 4.9、基于请求参数
  • 5、自定义断言工厂
  • 6、内置过滤器工厂(局部)
  • 6.1、添加请求头
  • 6.2、添加请求参数
  • 6.3、为匹配的路由统一添加前缀
  • 6.4、重定向
  • 7、自定义过滤器工厂(局部)
  • 8、全局过滤器
  • 9、自定义全局过滤器
  • 10、请求日志记录(Reactor Netty访问日志)
  • 11、跨域相关配置
  • 12、整合sentinel流控降级


1、什么是Spring Cloud Gateway

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

1.2、Spring Cloud Gateway功能特征

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

1.3、核心概念

  • 路由(route)

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

  • 断言(predicates)

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

  • 过滤器(Filter)

SpringCloud Gateway中的filter分为Gateway Filter和Global Filter,Filter可以对请求和响应进行处理。

2、Gateway初体验

2.1、Gateway快速开始

1、引入依赖

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

2、application.yml

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

浏览器进行访问http://localhost:8889/order-server/order/orderList即可转发到你的http://localhost:8004/order/orderList

3、Gateway整合nacos

增加一个依赖

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

修改yml文件

server:
  port: 8889
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: order_route    
          uri: lb://order-server #lb表示使用nacos本地的负载均衡策略
          predicates:
            - Path=/order-server/**
          filters:
            - StripPrefix=1
    #配置nacos        
    nacos:
      discovery:
        server-addr: 127.0.0.1:8847
        username: nacos
        password: nacos

技巧:断言和过滤路由简写方式

yml编写方式

server:
  port: 8889
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 120.0.0.1:8847
        username: nacos
        password: nacos
    gateway:
      discovery:
        locator:
          enabled: true #自动识别nacos上的服务

运行即可,发现我们服务可以正常访问。

4、内置断言工厂

4.1、基于Datetime类型

此类型断言根据时间做判断,主要由三个:

AfterRoutePredicateFactory:接受一个日期参数,判断请求日期是否晚于指定日期

BeforeRoutePredicateFactory:接受一个日期参数,判断请求日期是否早于指定日期

BetweenRoutePredicateFactory:接受一个日期参数,判断请求日期是否在指定时间段内

案例一(晚于指定日期)

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

案例二(早于指定日期)

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

案例三(指定日期之间)

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: https://example.org
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

4.2、基于远程地址

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

- RemoteAddr=192.168.1.1/24

4.3、基于Cookie

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

- Cookie=chocolate, ch.p

4.4、基于Header

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

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

4.5、基于Host

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

- Host=**.somehost.org,**.anotherhost.org

4.6、基于Method请求方法

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

- Method=GET,POST

4.7、基于Path匹配请求路径

{segment}:需要携带参数

- Path=/red/{segment},/blue/{segment}

例: /red/1 or /red/aaa or /blue/red

4.8、基于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

4.9、基于请求参数

- Query=green

例入: ?green=121

- Query=red, gree.

例入:?red=gree,值需要匹配后面的正则

5、自定义断言工厂

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

要求

  • 必须spring组件bean
  • 类必须加上RoutePredicateFactory作为结尾
  • 继承AbstractRoutePredicateFactory
  • 必须声明静态内部类,声明属性来接受 配置文件中的信息
  • 需要结合shortcutFieldOrder来进行绑定
  • 通过apply进行逻辑编写,true就是匹配成功,false就是匹配失败
@Component
public class CheckNameRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckNameRoutePredicateFactory.Config> {

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

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

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

        };
    }

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

        public String getName() {
            return name;
        }

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

}

yml文件加上

- CheckName=YangLi

测试即可。

6、内置过滤器工厂(局部)

由于内置过滤器工厂太多,我这里随便列几个,想看全部内置过滤器工厂,可去官网查看

6.1、添加请求头

- AddRequestHeader=X-Request-red, red

进行测试

@RequestMapping("/header")
public String header(@RequestHeader("X-Request-red") String color){
    return color;
}

结果

成功返回red

6.2、添加请求参数

- AddRequestParameter=color, blue

进行测试

@RequestMapping("/parameter")
public String parameter(@RequestParam("color") String color){
    return color;
}

结果

成功返回blue

6.3、为匹配的路由统一添加前缀

- PrefixPath=/mall-order #前缀,对应微服务需要配置context-path

mall-order中需要配置

server:
  servlet:
    context-path: /mall-order

6.4、重定向

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

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

  • 继承AbstractGatewayFilterFactory
  • 自定义的名称要以GatewayFilterFactory结尾
  • 交给spring管理

例子:请求后面的参数name=YangLi才可以

- CheckAuth=YangLi #自定义拦截器,必须?name=YangLi
@Component
public class
CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {
    public CheckAuthGatewayFilterFactory() {
        super(CheckAuthGatewayFilterFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("value");
    }
    @Override
    public GatewayFilter apply(CheckAuthGatewayFilterFactory.Config config) {
        return (exchange, chain) -> {
            String name = exchange.getRequest().getQueryParams().getFirst("name");
            if (StringUtils.isNotBlank(name)){
                if (config.getValue().equals(name)){
                    return  chain.filter(exchange);
                }else{
                    // 返回404
                    exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                    return exchange.getResponse().setComplete();
                }
            }
            return chain.filter(exchange);
        };
    }
    /**
     * 用于接受配置文件中 过滤器的信息
     */
    @Validated
    public static class Config {
        private String value;

        public String getValue() {
            return value;
        }

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

}

8、全局过滤器

springcloud gateway间歇性500异常解决 springcloud gateway lb_spring

局部过滤器和全局过滤器的区别?

局部:局部针对某个路由,需要在路由中进行配置

全局:针对所有路由请求,一旦定义就会全局使用

9、自定义全局过滤器

只需要实现GlobalFilter接口即可

@Component
public class LogGlobalFilter implements GlobalFilter {
    Logger logger = LoggerFactory.getLogger(this.getClass());

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

10、请求日志记录(Reactor Netty访问日志)

要启用Reactor Netty访问日志,请设置-Dreactor.netty.http.server.accessLogEnabled=true

它是java系统属性,不是Spring Boot属性

springcloud gateway间歇性500异常解决 springcloud gateway lb_gateway_02

进行访问,结果会打印日志信息

2022-07-01 15:32:12.140  INFO 15400 --- [ctor-http-nio-2] reactor.netty.http.server.AccessLog      : 0:0:0:0:0:0:0:1 - - [01/Jul/2022:15:32:11 +0800] "GET /order-server/order/parameter HTTP/1.1" 200 4 8889 942 ms

11、跨域相关配置

前后端分离需要跨域,所以我们直接在网关这里进行配置

yml方式配置

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

配置类的方式

@Configuration
public class CorsConfig {
    @Bean
    public CorsWebFilter corsWebFilter(){
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 允许的请求头参数
        corsConfiguration.addAllowedHeader("*");
        // 允许的method
        corsConfiguration.addAllowedMethod("*");
        // 允许的来源
        corsConfiguration.addAllowedOrigin("*");
        // 允许访问的资源
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);

    }
}

12、整合sentinel流控降级

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

1、添加依赖

<!--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>

2、添加配置

sentinel:
      transport:
        dashboard: 127.0.0.1:8010

3、运行并进入控制台

springcloud gateway间歇性500异常解决 springcloud gateway lb_spring_03

测试简单流控规则(一秒钟访问两次将会被限流):

springcloud gateway间歇性500异常解决 springcloud gateway lb_spring_04

在浏览器进行访问并快速刷新,触发流控规则

springcloud gateway间歇性500异常解决 springcloud gateway lb_spring cloud_05

名词解读

  • Busrst size 宽容次数,比如设置为1,表示一秒钟访问3次以上被限流
  • 间隔 比如间隔设置为2,则表示2秒时间内访问次数大于2则被流控

针对请求属性

springcloud gateway间歇性500异常解决 springcloud gateway lb_Cloud_06

Client IP 根据客户端ip地址进行流控

  • 精确 ------- 精确匹配你填的匹配串进行流控
  • 子串 --------包含匹配串
  • 正则 --------- 可以填入正则表达式

Remote Host 根据远程域名进行流控

Header:根据请求头名字匹配对应值进行流控

springcloud gateway间歇性500异常解决 springcloud gateway lb_gateway_07

URL参数和Cookie方式与上述雷同


新建api分组(针对API接口流控)

springcloud gateway间歇性500异常解决 springcloud gateway lb_gateway_08

新增流控规则,选择刚刚新加的api分组

springcloud gateway间歇性500异常解决 springcloud gateway lb_Cloud_09

进行测试发现,针对这两个接口发生流控,其他接口无流控

降级规则

springcloud gateway间歇性500异常解决 springcloud gateway lb_spring_10

当然,相应api也可以配置降级规则

系统规则

springcloud gateway间歇性500异常解决 springcloud gateway lb_自定义_11

这个与sentinel配置系统保护用法一致。

自定义流控异常返回信息

配置类方式:

@Configuration
public class GatewayConfig {
    @PostConstruct
    public void init(){
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                HashMap<String,String> map = new HashMap<>(10);
                map.put("code", HttpStatus.TOO_MANY_REQUESTS.toString());
                map.put("message","限流了");
                // 自定义异常处理
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

springcloud gateway间歇性500异常解决 springcloud gateway lb_自定义_12

yml方式

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      scg:
        fallback:
          mode: response
          response-body: '{"code":403,"mess":"限流了"}'

springcloud gateway间歇性500异常解决 springcloud gateway lb_spring cloud_13