Gateway简介

Gateway概念

Gateway是Spring Cloud推出的全新网关技术,其目标是替代 Zuul(Zuul1.0性能较差、Zuul2.0的开发团队出现内部问题导致迟迟无法推出)。Spring Cloud为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。Spring Cloud Gateway 的目标,不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全、监控和限流。

Springcloud中所集成的Zuul版本,采用的是Tomcat容器,使用的是传统的Servlet IO处理模型。servlet由servlet container进行生命周期管理。container启动时构造servlet对象并调用servlet init()进行初始化;container关闭时调用servlet destory()销毁servlet;container运行时接受请求,并为每个请求分配一个线程(一般从线程池中获取空闲线程)然后调用service()。

servlet是一个简单的网络IO模型,当请求进入servlet container时,servlet container就会为其绑定一个线程,在并发不高的场景下这种模型是适用的,但是一旦并发上升,线程数量就会上涨,而线程资源代价是昂贵的(上线文切换,内存消耗大)严重影响请求的处理时间。在一些简单的业务场景下,不希望为每个request分配一个线程,只需要1个或几个线程就能应对极大并发的请求,这种业务场景下servlet模型没有优势。
image.png
所以Springcloud Zuul 是基于servlet之上的一个阻塞式处理模型,即spring实现了处理所有request请求的一个servlet(DispatcherServlet),并由该servlet阻塞式处理处理。所以Springcloud Zuul无法摆脱servlet模型的弊端。虽然Zuul 2.0开始,使用了Netty,并且已经有了大规模Zuul 2.0集群部署的成熟案例,但是,Springcloud官方已经没有集成改版本的计划了。

Gateway特征

下面是Spring Cloud官方给出的Gateway的特征:

  1. 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
  2. 集成 Hystrix 断路器
  3. 集成 Spring Cloud DiscoveryClient
  4. Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters
  5. 具备一些网关的高级功能:动态路由、限流、路径重写

从特征方法来说,Gateway和Zuul的特征是差不多的,主要区别还是在Gateway使用了高性能的底层通信框架Netty。
image.png

搭建Gateway网关

Gateway工作流程

关键概念

在介绍工作流程之前,需要了解三个关键词

  • 路由(Route):路由是网关最基础也是最核心的部分,路由信息由路由ID、目标URI、一组断言和一组过滤器组成。如果断言为true则说明请求的URI和其它配置向匹配,然后进行路由转发
  • 断言(Predicate):Gateway中的断言是用于开发者去定义匹配来自Http Request中的任何信息的,比如可以定义匹配请求头、参数、时间……
  • 过滤器(Filter):Gateway把过滤器分为Pre Filter和Post Filter,前者在请求时进行过滤,后者在响应时进行过滤

    流程图

    image.png
  1. Gateway Client向Gateway网关发送请求
  2. 请求首先被HttpWebHandlerAdapter处理成功网关上下文
  3. 接收请求给到DispatcherHandler,它负责路由查找,并根据路由断言判断路由是否可用
  4. 如果路由断言通过,进入FilteringWebHandler过滤链;否则拒绝用户的访问请求
  5. 首先进入Pre Filter,此时会进行一些鉴权、日志打印等其它操作
  6. 使用负载均衡算法访问对应的微服务
  7. 返回响应结果,响应结果在Post Filter,期间也会进行其它操作
  8. 响应结果返回给用户

    路由

    路由规则

    下面这张图是全部的路由规则:
    do9wrc3892.jpeg
    这些规则有些用的并不是很多,下面介绍几个用的比较多的规则:

    Path

    1. spring:
    2. cloud:
    3. gateway:
    4. discovery:
    5. locator:
    6. enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
    7. routes:
    8. - id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
    9. uri: http://localhost:8001/ #匹配后提供服务的路由地址
    10. predicates:
    11. - Path=/payment/** # 断言,路径相匹配的进行路由

    注意: 上面的配置中,routes(路由)和predicates(断言)都是数组,可用有多个元素,所以其元素前面都需要加上“-”

上面这个路由,“-id”表示路由ID是“payment_routh”;“uri”表示断言成功后路由转发的地址;“-Path”是Path路由规则,表示请求URI形式需要“/payment/**”才会进行路由转发。比如我访问“http://localhost:9527/payment/get/12”,它会转发到“http://localhost:8001/payment/get/12”
其中“localhost:9527”是该网关的ip和端口。

Query

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001/          #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/**         # 断言,路径相匹配的进行路由
            - Query=u_id        # 断言,参数相匹配的进行路由

Query规则表示URI必须携带指定Query参数,如上面的:u_id,否则不会进行路由转发。同时也可以给这个参数指定正则表达式,如:

predicates:
    - Query=u_id, 123.        # 断言,路径相匹配的进行路由

表示u_id的格式必须为123开头,“.”表示是任何字符

Method

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001/          #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/**         # 断言,路径相匹配的进行路由
            - Method=POST        # 断言,请求方法相匹配的进行路由

上述配置表示只有POST方式的请求可以通过断言

DateTime

DateTime规则包括After、Before、Between,下面介绍一下Before,其它类似:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001/          #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/**         # 断言,路径相匹配的进行路由
            - After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]

表示只有上海时间“2020-03-08T10:59:34.102+08:00”之后的请求才能路由转发

Header

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001/          #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/**         # 断言,路径相匹配的进行路由
            - Header=Authorization

上述表示只有携带Authorization请求的请求可用路由转发,同时也可以设置正则表达式

动态路由

在前面的路由中,指定了断言之后的具体的ip和端口号,如果一个微服务实现了集群,很明显这种方式是行不通的,所以在集群环境下需要动态路由URI,如下所示:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service    #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/**         # 断言,路径相匹配的进行路由

其中lb表示实现负载均衡,默认的负载均衡算法是轮询,比如上面的配置,断言成功会匹配到“cloud-payment-service”的微服务并实现负载均衡。如:
image.png
image.png
第一次路由到8001,第二次就路由到8002了

除了手动配置动态路由,还可以根据注册中心的微服务的名称进行自动动态路由,如下配置:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
          lower-case-service-id: true #是否将注册中心的服务名转小写

访问“http://localhost:9527/payment/get/12”:
image.png
404的原因其实是约定大于配置,Spring Cloud约定如果使用自动路由到注册中心的微服务的话,根URI必须是微服务名称的小写形式,如这样加上一个根路径就可以了:
image.png

过滤器

过滤器也是Gateway的核心功能,在 Gateway 的过滤器中又可以分为局部过滤器和全局过滤器。听名称就知道其作用,局部过滤器是用于某一个路由上的,而全局过滤器是用于所有路由上的。不过不管是 过滤器局部还是全局过滤器,生命周期都分为 pre 和 post,如下图所示:
image.png

  • pre:作用于路由到微服务之前调用。我们可以利用这种过滤器实现身份验证、在集群中选择请求的微服务,记录调试记录等
  • post:作用于路由到微服务之后执行。我们可以利用这种过滤器用来响应添加标准的 HTTP Header,收集统计信息和指标、将响应从微服务发送到客户端。

    局部过滤器

    Gateway局部过滤器

    下面是Gateway提供的几个路由局部过滤器,由于用的不多,简单了解即可
StripPrefix 用于截断原始请求的路径 使用数字表示要截断的路径数量
AddRequestHeader 为原始请求添加 Header Header 的名称及值
AddRequestParameter 为原始请求添加请求参数 参数名称及值
Retry 针对不同的响应进行重试 reties、statuses、methods、series
RequestSize 设置允许接收最大请求包的大小 请求包大小,单位字节,默认5M
SetPath 修改原始请求的路径 修改后的路径
RewritePath 重写原始的请求路径 原始路径正则表达式以及重写后路径的正则表达式
PrefixPath 为原始请求路径添加前缀 前缀路径
RequestRateLimiter 对请求限流,限流算法为令牌桶 KeyResolver、reteLimiter、statusCode、denyEmptyKey

RewritePath
image.png
SetPath
image.pngStripPrefix
image.png
PrefixPath
image.png
AddRequestParameter
image.png

自定义局部过滤器

全局过滤器

Gateway全局过滤器

全局过滤器作用于所有路由,无需配置,Gateway提供的全局过滤器已经自动生效了,已经帮我们完成了很多网关功能。下面是几个常见的Gateway全局过滤器:

ForwardPathFilter / ForwardRoutingFilter 路径转发相关过滤器
LoadBalanceerClientFilter 负载均衡客户端相关过滤器
NettyRoutingFilter / NettyWriteResponseFilter Http 客户端相关过滤器
RouteToRequestUrlFilter 路由 URL 相关过滤器
WebClientHttpRoutingFilter / WebClientWriteResponseFilter 请求 WebClient 客户端转发请求真实的URL并将响应写入到当前的请求响应中
WebsocketRoutingFilter websocket 相关过滤器

自定义全局过滤器