Spring Cloud Gateway 网关服务教程(一)

3,539 阅读5分钟

本文使用的 Spring Cloud 版本为 2.1.2.RELEASE,如下:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gateway-core</artifactId>
    <version>2.1.2.RELEASE<version>
</dependency>

一、如何使用Gateway功能

通过YAML配置文件

spring:
  cloud:
    gateway:
      enabled: true
      routes:
        - id: path_route
          order: 500
          predicates:
            - Path=/demo/list
          filters:
            - name: Retry
              args:
                retries: 1
          uri: http://127.0.0.1:8080/demo/list

通过RouteLocatorBuilder注册

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder
            .routes()
            .route("path_route", r -> r.order(500).path("/demo/list").filters(f -> f.retry(1)).uri("http://127.0.0.1:8080/demo/list"))
            .build();
}

使用说明

上述两种方式完全等价,它们建立了相同的“路由规则(RouteDefinition)”,该规则定义如下:

  • id为path_route
  • 优先级order为500
  • 断言器使用PathRoutePredicateFactory匹配/demo/list路径
  • 拦截器使用RetryGatewayFilterFactory在失败时进行1次重试
  • 匹配成功后请求uri为http://127.0.0.1:8080/demo/list地址。

由此可见,路由规则包含5个部分,分别是id、order、predicates、filters、uri。

  • 在定义predicates时,可以通过继承AbstractRoutePredicateFactory实现自定义的断言器,如果自定义的断言器在YAML配置文件中使用,命名需要遵循特殊规范,如示例中的PathRoutePredicateFactory断言器,在文件用Path=/demo/list表示,“Path”为PathRoutePredicateFactory的“RoutePredicateFactory”的前缀名,“/demo/list”为参数。
  • 在定义filters时,可以通过继承AbstractGatewayFilterFactory实现自定义拦截器,YAML配置文件中使用时,同样遵循上述命名规范和使用规则。

二、实现自定义的Gateway组件

Spring Cloud Gateway的核心功能由断言器、拦截器实现,下面将详细讲解如何实现自己的断言器和拦截器。

RoutePredicateFactory断言器工厂

1. 自定义的RoutePredicateFactory断言器工厂需要通过继承AbstractRoutePredicateFactory实现
@Component
public class CustomRoutePredicateFactory extends AbstractRoutePredicateFactory<CustomRoutePredicateFactory.Config> {

    public CustomRoutePredicateFactory() {
        // 指定apply方法参数的类型,需要与AbstractRoutePredicateFactory的泛型一致
        super(CustomRoutePredicateFactory.Config.class);
    }

    @Override
    public ShortcutType shortcutType() {
        // 使用yaml配置文件时的数据映射方式
        return ShortcutType.GATHER_LIST;
    }

    @Override
    public List<String> shortcutFieldOrder() {
        // 使用GATHER_LIST映射方式时,指定对应List的变量名(即Config对象中的List变量)
        return Collections.singletonList("list");
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        // config的变量值,框架会根据yaml配置文件进行设置
        return exchange -> {
            // 自定义的断言规则,返回true则表示断言匹配成功
            for (String pattern : config.getList()) {
                if (exchange.getRequest().getURI().getPath().startsWith(pattern)) {
                    return true;
                }
            }
            return false;
        };
    }

    public static class Config {

        private List<String> list = new ArrayList<>();

        public List<String> getList() {
            return list;
        }

        public void setList(List<String> list) {
            this.list = list;
        }

    }

}

下面将详细说明示例代码中各个方法的作用:

  • 构造方法CustomRoutePredicateFactory(),内部调用了super(configClass)方法,传入了CustomRoutePredicateFactory.Config.class对象,用于指定apply(config)方法参数的类型,框架根据该class类型自动生成config对象。
  • shortcutType()方法,返回值类型ShortcutType共有三个属性,各个属性决定了框架自动生成config对象时的规则。当ShortcutType.DEFAULT类型时,框架将yaml中的值,映射到config中名称相同的变量中;当ShortcutType.GATHER_LIST类型时,框架将yaml中的值聚合为List,映射到shortcutFieldOrder()的返回值对应config的变量中,此时shortcutFieldOrder()返回的List的长度必须为1。
  • shortcutFieldOrder()方法,当shortcutType()返回值为ShortcutType.GATHER_LIST时被使用,用于指定Config中List变量的名称。
  • apply(config)方法,返回值Predicate为实际的RoutePredicate断言器。
2. 使用自定义的RoutePredicateFactory断言器工厂

2.1 通过YAML配置文件
需要注意自定义的断言器工厂命名必须为以RoutePredicateFactory作为后缀,而前面的部分为断言器工厂的name值。

spring:
  cloud:
    gateway:
      enabled: true
      routes:
        - id: custom_route
          predicates:
            - name: Custom
              args:
                arg1: /demo/list
          uri: http://127.0.0.1:8080/demo/list

2.2 通过RouteLocatorBuilder注册

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder, CustomRoutePredicateFactory customRoutePredicateFactory) {
    CustomRoutePredicateFactory.Config config = new CustomRoutePredicateFactory.Config();
    config.setList(Lists.newArrayList("/demo/list"));
    return builder
            .routes()
            .route("custom_route", r -> r.predicate(customRoutePredicateFactory.apply(config)).uri("http://127.0.0.1:8080/demo/list"))
            .build();
}
3. 自定义Predicate断言器

3.1 通过RouteLocatorBuilder注册

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder, CustomRoutePredicateFactory customRoutePredicateFactory) {
    List<String> paths = new ArrayList<>();
    paths.add("/demo/list");
    return builder
            .routes()
            .route("path_route", r -> r.predicate(exchange -> {
                for (String pattern : paths) {
                    if (exchange.getRequest().getURI().getPath().startsWith(pattern)) {
                        return true;
                    }
                }
                return false;
            }).uri("http://127.0.0.1:8080/demo/list"))
            .build();
}

由此可见,Predicate断言器是是Gateway匹配请求的核心,当Predicate.test(exchange)方法返回true时匹配成功。

GatewayFilterFactory拦截器工厂

1. 自定义的GatewayFilter拦截器需要通过继承AbstractGatewayFilterFactory实现。
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.Config> {

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

    @Override
    public GatewayFilter apply(Config config) {
        return new CustomGatewayFilter(config);
    }

    public static class CustomGatewayFilter implements GatewayFilter, Ordered {

        private Config config;

        public CustomGatewayFilter(Config config) {
            this.config = config;
        }

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

            if (config.isGray()) {
                return chain.filter(exchange);
            }

            return chain.filter(exchange);
        }

        @Override
        public int getOrder() {
            return 100;
        }

    }


    public static class Config {

        private boolean gray = false;

        public boolean isGray() {
            return gray;
        }

        public void setGray(boolean gray) {
            this.gray = gray;
        }

    }

}

定义方式与RoutePredicateFactory基本相同,在需要对GatewayFilter设置优先级时需要实现Ordered接口。

2. 使用自定义的GatewayFilterFactory拦截器工厂

2.1 通过YAML配置文件
需要注意自定义的拦截器工厂命名必须为以GatewayFilterFactory作为后缀,而前面的部分为拦截器工厂的name值。

spring:
  cloud:
    gateway:
      enabled: true
      routes:
        - id: custom_route
          predicates:
            - name: Custom
              args:
                arg1: /demo/list
          filters:
            - name: Custom
              args:
                gray: true
          uri: http://127.0.0.1:8080/demo/list

使用方式与RoutePredicateFactory基本相同,但参数映射仅支持变量名方式。

GlobalFilter全局拦截器

1. 自定义的GlobalFilter拦截器需要通过继承GlobalFilter实现。
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {

    public static final Logger log = LoggerFactory.getLogger(CustomGlobalFilter.class);

    @Override
    public int getOrder() {
        return 0;
    }

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

        String path = exchange.getRequest().getURI().getRawPath();
        log.info("API访问记录 - url={}", path);

        return chain.filter(exchange);
    }

}

GlobalFilter无需工厂类,实现GlobalFilter接口即可。排序需要实现Ordered接口。

三、说明

  • Spring Cloud Gateway的核心逻辑,将以阅读源码的方式在后续文章讲解。
  • 网关服务实现负载均衡、流量控制、请求鉴权,也将在后续的文章中讲解。