# Spring Cloud Gateway

万世威 2020-10-16

# 基本介绍

# 功能特性

  • 基于 Spring 5, Spring Boot 2 and Project Reactor

  • 动态路由

  • Predicates 和 filters 作用于特定路由

  • 集成了 Hystrix 断路器

  • 集成了 Spring Cloud DiscoveryClient

  • 易于编写的 Predicates 和 Filters

  • 限流

  • 路径重写

# 缺点

  • 依赖 Netty 与 webflux,不是传统 servlet,与 webmvc 不兼容
  • 不能部署到 tomcat、jetty 等 servlet 容器中,不能部署成 war 包,只能打成 jar 包执行

# 核心概念

  • 路由(routes):网关的基本构成块,路由信息由一个 ID、一个目的 URI、一组断言、一组过滤器组成,如果断言为真,则请求 URL 匹配到该路由
  • 断言(Predicates):可匹配 Http Reuest 中的任何信息(路由执行的条件)
  • 过滤器(Filters):分为 Global Filter(全局) 和 Gateway Filter(针对某一个路由)

# 工作流程

spring_cloud_gateway_diagram

# 基本使用

# 基本配置

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
1
2
3
4
spring:
  application:
    name: nacos-gateway-server
  cloud:
    gateway:
      routes:
        - id: provider
          uri: http://127.0.0.1:9002/
          predicates:
            - Path=/dist/** # url 中匹配这个地址后,转发到 uri 上,uri+Path 组成
1
2
3
4
5
6
7
8
9
10

# 动态路由

问题:基本配置里面硬编码 uri 地址,不能做到负载、服务自动发现

解决:配置之前的 Nacos 动态服务功能实现

# 配置

<!-- 引入依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
1
2
3
4
5
/* main 方法上开启服务发现功能 */
@EnableDiscoveryClient
public class GatewayMain {}
1
2
3
spring:
  application:
    name: nacos-gateway-server
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        - id: provider
          #        uri: http://127.0.0.1:9002/
          uri: lb://quick-provider #配置提供者的注册的服务名spring.application.name
          predicates:
            - Path=/dist/**
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 路由规则

# 组成

RoutePredicateFactory

# 示例

# after
spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
# cookie
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p
# method
spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

更多可参考官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/single/spring-cloud-gateway.html#gateway-request-predicates-factories

# 自定义 Predicate

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

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

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

    @Override
    public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {
        return serverWebExchange -> {
            String ageStr = serverWebExchange.getRequest().getQueryParams().getFirst("age");
            if (!StringUtils.isEmpty(ageStr)) {
                int age = Integer.parseInt(ageStr);
                return age >= config.getMinAge() && age <= config.getMaxAge();
            }
            return false;
        };
    }

    public static class Config {
        private int minAge;
        private int maxAge;
        public Config() { }
      	// 省略 getter、setter
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
predicates:
  - Age=18,30
1
2

1、Config 类 必须要有 构造函数

2、配置中 Age 和类名保持一致,且类名格式固定,以 RoutePredicateFactory 结尾

3、18-30,按照 shortcutFieldOrder 方法参数顺序注入值

4、具体可参考内置的 Predicate Factory,采用 xxxRoutePredicateFactory

# 过滤器

# 生命周期

  • pre:在请求之前,可用来记录调用信息、鉴权等
  • post:在请求之后执行,可用来添加 Header、收集某些指标信息等

# 过滤器类型

  • Global Filter:应用到所有 route
  • Gateway Filter:应用于单独的一个 route

# Gateway Filter

# 文档

​ https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/single/spring-cloud-gateway.html#_gatewayfilter_factories

# 自定义过滤器

/* 使用此方式可在 yaml 中配置 */

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


    private static final Log log = LogFactory.getLog(GatewayFilter.class);
    private static final String REQUEST_TIME_BEGIN = "requestTimeBegin";
    private static final String KEY = "withParams";

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

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

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis());
            return chain.filter(exchange).then(
                    Mono.fromRunnable(() -> {
                        Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN);
                        if (startTime != null) {
                            StringBuilder sb = new StringBuilder(exchange.getRequest().getURI().getRawPath())
                                    .append(": ")
                                    .append(System.currentTimeMillis() - startTime)
                                    .append("ms");
                            if (config.isWithParams()) {
                                sb.append(" params:").append(exchange.getRequest().getQueryParams());
                            }
                            log.info(sb.toString());
                        }
                    })
            );
        };
    }


    public static class Config {
        private boolean withParams;
        public boolean isWithParams() {
            return withParams;
        }
        public void setWithParams(boolean withParams) {
            this.withParams = withParams;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
gateway:
	routes:
	- id: provider
		uri: lb://quick-provider
    filters:
    - RequestTime=true
    predicates:
    - Path=/dist/**
1
2
3
4
5
6
7
8

# Global Filter

# 文档

​ https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/single/spring-cloud-gateway.html#_global_filters

# 自定义全局过滤器

public class AuthFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("invoke auth filter..");
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        return 0;// 返回值越小,优先级越高
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 其他配置

  • 全局开关: spring.cloud.gateway.enabled=false

# 参考文档

  • https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/single/spring-cloud-gateway.html

  • https://github.com/spring-cloud/spring-cloud-gateway