写点什么

【SpringCloud 技术专题】「Gateway 网关系列」(2)微服务网关服务的 Gateway 功能配置指南分析

作者:浩宇天尚
  • 2021 年 12 月 13 日
  • 本文字数:5090 字

    阅读完需:约 17 分钟

【SpringCloud技术专题】「Gateway网关系列」(2)微服务网关服务的Gateway功能配置指南分析

Spring Cloud Gateway 简介

  • Spring Cloud Gateway 是 Spring Cloud 体系的第二代网关组件,基于 Spring 5.0 的新特性 WebFlux 进行开发,底层网络通信框架使用的是 Netty,所以其吞吐量高、性能强劲,未来将会取代第一代的网关组件 Zuul。

  • Spring Cloud Gateway 可以通过服务发现组件自动转发请求,默认集成了 Ribbon 做负载均衡,以及默认使用 Hystrix 对网关进行保护,当然也可以选择其他的容错组件,例如 Sentinel。

SpringCloud Gateway 优点

  • 性能强劲:是第一代网关 Zuul 的 1.6 倍

  • 功能强大:内置了很多实用的功能,例如转发、监控、限流等

  • 设计优雅,容易扩展

SpringCloud Gateway 缺点

  • 学习复杂度:其实现依赖 Netty 与 WebFlux,不是传统的 Servlet 编程模型,有一定的学习成本

  • 部署特殊性:不能在 Servlet 容器下工作,也不能构建成 WAR 包,即不能将其部署在 Tomcat、Jetty 等 Servlet 容器里,只能打成 jar 包执行,不支持 Spring Boot 1.x,需 2.0 及更高的版本

核心概念

Route(路由)

Spring Cloud Gateway 的基础元素,可简单理解成一条转发规则。包含:路由 ID、目标 URL、Predicate 集合以及 Filter 集合。


Gateway 路由配置案例:


spring:  cloud:    gateway:      routes:      - id: server-center  # 唯一标识,通常使用服务id        uri: lb://server-center  # 目标URL,lb代表从注册中心获取服务,lb是Load Balance的缩写        predicates:   # Predicate集合          - Path=/sample/cloud/v1/server-center/**  # 匹配转发路径        # Filter集合        filters:        # 从第几级开始转发          - StripPrefix=4
复制代码
Predicate(谓词)

即 java.util.function.Predicate 这个接口,Gateway 使用 Predicate 实现路由的匹配条件

Filter(过滤器)

与我们平时使用的 Servlet 编程模型里的过滤器概念类似,同样可以用于修改请求以及响应数据,可以利用 Filter 实现鉴权、访问日志记录,接口耗时记录等功能

Spring Cloud Gateway 架构图

流程介绍:

Gateway Client 发送请求给 Spring Cloud Gateway,Gateway Handler Mapping 会判断请求的路径是否匹配路由的配置,如果匹配则会进入 Gateway Web Handler,Web Handler 会读取路由上所配置的过滤器,然后将该请求交给过滤器去处理,最后转发到路由配置的微服务上。


  • Gateway Client:泛指外部请求,例如浏览器、app、小程序等

  • Proxied Service:指的是被网关代理的微服务

相关源码:
  • Gateway Handler Mapping:org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping

  • Gateway Web Handler:org.springframework.cloud.gateway.handler.FilteringWebHandler

实际开发案例

创建 Spring Cloud Gateway 项目

这里使用 IDEA 的 Spring Initializr 进行项目的创建,到选择依赖这一步勾选 gateway 依赖,如下图:



网关组件一般都配合服务发现组件使用,我这里使用 Nacos 作为服务发现组件,具体的依赖如下(2.1.x 为例):


<dependencies>    <dependency>        <groupId>org.springframework.cloud</groupId>        <artifactId>spring-cloud-starter-gateway</artifactId>    </dependency>    <!-- Nacos Client -->    <dependency>        <groupId>com.alibaba.cloud</groupId>        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>    </dependency>    <!-- actuator -->    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-actuator</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-test</artifactId>        <scope>test</scope>    </dependency></dependencies>
<dependencyManagement> <dependencies> <!--整合Spring Cloud--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR2</version> <type>pom</type> <scope>import</scope> </dependency> <!--整合Spring Cloud Alibaba--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies></dependencyManagement>
复制代码


然后编写配置文件内容如下:


server:  port: 8040spring:  application:    name: gateway  cloud:    nacos:      discovery:        # 指定nacos server的地址        server-addr: 127.0.0.1:8848    gateway:      discovery:        locator:          # gateway通过服务发现组件找到其他的微服务,从而自动转发请求          enabled: true# actuator相关配置management:  endpoints:    web:      exposure:        # 暴露所有监控端点        include: '*'  endpoint:    health:      # 总是显示健康检测详情      show-details: always
复制代码


完成以上步骤后,我们来启动这个网关服务,进行一个简单的测试,看看是否能将请求正常地转发到指定的微服务上。


此时有一个名为 server-center 的微服务,该微服务有一个按 id 获取用户信息的接口,接口路径为/users/{id}。


若通过网关服务来访问这个接口,要如何做呢?很简单,gateway 配合服务发现组件使用时,会有一个默认的转发规则,如下:


${GATEWAY_URL}/{微服务名称}/{接口路径}
复制代码


按该规则得出来的具体 url 为:localhost:port/server-center/users/{id},gateway 可以根据 url 上的微服务名称将访问请求转发到该微服务上。

自定义路由的注意事项:

predicates 配置项必须有,且必须配置一个及以上的 Predicate,但不一定非要配置 Path,可以配置其他的 Predicate,例如 After、Before 等,此时 Path 的默认值为/**

路由配置的两种形式

Spring Cloud Gateway 的路由配置有两种形式,分别是路由到指定的 URL 以及路由到指定的微服务。


在这两种形式中,均支持访问路径的通配及精确匹配,在之前的示例中我们只使用了通配。这里将给出具体的配置示例,以此直观的了解这两种形式及不同匹配方式在配置上的区别。

路由到指定的 URL

通配,使用通配符/**进行匹配,示例:


spring:  cloud:    gateway:      routes:        - id: test_route  # 路由的唯一标识          uri: http://www.xxx.com          predicates:            # 使用通配符匹配            - Path=/**
复制代码


该配置使访问 GATEWAY_URL/** 时会转发到 http://www.xxx.com/

精确匹配

配置具体的接口路径即可,示例:


spring:  cloud:    gateway:      routes:        - id: test_route  # 路由的唯一标识          uri: http://www.xxx.com/user/order/detail          predicates:            # 指定具体的路径进行匹配            - Path=/user/order/detail
复制代码


该配置使访问 GATEWAY_URL/user/order/detail 时会转发到 ttp://www.xxx.com/user/order/detail

路由到指定的微服务

通配,示例:


spring:  cloud:    gateway:      routes:        - id: server-center  # 路由唯一标识,这种形式下通常是微服务名称          uri: lb://server-center  # lb代表从注册中心获取服务          predicates:            # 使用通配符匹配            - Path=/**
复制代码


该配置使访问 GATEWAY_URL/** 时会转发到 server-center 微服务的/**精确匹配,示例:


spring:  cloud:    gateway:      routes:        - id: server-center  # 路由的唯一标识,这种形式下通常是微服务名称          uri: lb://server-center/users/info  # lb代表从注册中心获取服务          predicates:            # 指定具体的路径进行匹配            - Path=/users/info
复制代码


该配置使访问 GATEWAY_URL/users/info 时会转发到 server-center 微服务的/users/info。

路由谓词工厂

前面提到过谓词是路由的判断条件,而路由谓词工厂就是作用到指定路由上的一堆谓词判断条件。在之前的示例里,我们就已经使用过路由谓词工厂了,就是自定义转发路径时所配置的 Path。

内置的路由谓词工厂

Spring Cloud Gateway 内置了众多路由谓词工厂,这些路由谓词工厂为路由匹配的判断提供了有力的支持,而我们之前所使用的 Path 就是内置的路由谓词工厂之一,用于判断当前访问的接口路径是否与该路由所配置的路径相匹配,若匹配则进行转发。


由于 Gateway 内置的路由谓词工厂比较多,篇幅有限就不在本文中介绍了,可以参考另一篇文章:

自定义路由谓词工厂

Spring Cloud Gateway 内置了一系列的路由谓词工厂,但如果这些内置的路由谓词工厂不能满足业务需求的话,我们可以自定义路由谓词工厂来实现特定的需求。


例如有某个服务限制用户只允许在 09:00 - 17:00 这个时间段内才可以访问,内置的路由谓词工厂是无法满足这个需求的,所以此时我们就需要自定义能够实现该需求的路由谓词工厂。


首先定义一个配置类,用于承载时间段的配置参数:


@Datapublic class TimeBetweenConfig {    /**     * 开始时间     */    private LocalTime start;
/** * 结束时间 */ private LocalTime end;}
复制代码


然后定义一个路由谓词工厂,具体代码如下:


/** * 路由谓词工厂必须以RoutePredicateFactory结尾, * 这是Spring Cloud Gateway的约定 **/@Slf4j@Componentpublic class TimeBetweenRoutePredicateFactory extends AbstractRoutePredicateFactory<TimeBetweenConfig> {
public TimeBetweenRoutePredicateFactory() { super(TimeBetweenConfig.class); }
/** * 实现谓词判断的方法 */ @Override public Predicate<ServerWebExchange> apply(TimeBetweenConfig config) { return exchange -> { LocalTime start = config.getStart(); LocalTime end = config.getEnd(); // 判断当前时间是否为允许访问的时间段内 LocalTime now = LocalTime.now(); return now.isAfter(start) && now.isBefore(end); }; }
/** * 控制配置类(TimeBetweenConfig)属性和配置文件中配置项(TimeBetween)的映射关系 */ @Override public List<String> shortcutFieldOrder() { /* * 例如我们的配置项是:TimeBetween=上午9:00, 下午5:00 * 那么按照顺序,start对应的是上午9:00;end对应的是下午5:00 **/ return Arrays.asList("start", "end"); }}
复制代码


最后需要在配置文件中启用该路由谓词工厂,并且需要禁止 gateway 通过服务发现组件转发请求到其他的微服务,修改 Gateway 相关配置如下:


spring:  cloud:    gateway:      discovery:        locator:          # 禁止gateway通过服务发现组件转发请求到其他的微服务          enabled: false      routes:        - id: server-center          # 目标URL,lb代表从注册中心获取服务          uri: lb://server-center          predicates:            # 注意名称必须为路由谓词工厂类名的前缀,参数为允许访问的时间段            - TimeBetween=上午9:00,下午5:00
复制代码


可以看到这里主要是配置了我们自定义的路由谓词工厂类名的前缀以及允许访问的时间段,这个时间格式不是随便配置的,而是 Spring Cloud Gateway 的默认时间格式,相关源码如下:


org.springframework.format.support.DefaultFormattingConversionService#addDefaultFormatters
复制代码


时间格式是可以注册的,关于时间格式注册的相关源码如下:


org.springframework.format.datetime.standard.DateTimeFormatterRegistrar#registerFormatters
复制代码


  • 这里之所以要禁止 gateway 通过服务发现组件转发请求到其他的微服务,是因为开启该配置项的话会导致我们自定义的路由谓词工厂不生效。

  • 不生效也是有原因的,开启该配置项会令 Gateway 优先将请求按照该配置项进行转发,那么我们自定义的路由就不会生效。


  • https://www.jianshu.com/p/97bf2aa1b4f7


  • https://blog.51cto.com/zero01/2430084


  • https://www.jianshu.com/p/1436a94ec8d5


  • https://blog.csdn.net/qq_38380025/article/details/102968559


  • https://www.jianshu.com/p/66d664787d21


  • https://www.jianshu.com/p/0813d35ae197

发布于: 3 小时前阅读数: 7
用户头像

浩宇天尚

关注

🏆 InfoQ写作平台-签约作者 🏆 2020.03.25 加入

【个人简介】酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“ 【技术格言】任何足够先进的技术都与魔法无异 【技术范畴】Java领域、Spring生态、MySQL专项、APM专题及微服务/分布式体系等

评论

发布
暂无评论
【SpringCloud技术专题】「Gateway网关系列」(2)微服务网关服务的Gateway功能配置指南分析