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模型没有优势。
所以Springcloud Zuul 是基于servlet之上的一个阻塞式处理模型,即spring实现了处理所有request请求的一个servlet(DispatcherServlet),并由该servlet阻塞式处理处理。所以Springcloud Zuul无法摆脱servlet模型的弊端。虽然Zuul 2.0开始,使用了Netty,并且已经有了大规模Zuul 2.0集群部署的成熟案例,但是,Springcloud官方已经没有集成改版本的计划了。
Gateway特征
下面是Spring Cloud官方给出的Gateway的特征:
- 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
- 集成 Hystrix 断路器
- 集成 Spring Cloud DiscoveryClient
- Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters
- 具备一些网关的高级功能:动态路由、限流、路径重写
从特征方法来说,Gateway和Zuul的特征是差不多的,主要区别还是在Gateway使用了高性能的底层通信框架Netty。
搭建Gateway网关
Gateway工作流程
关键概念
在介绍工作流程之前,需要了解三个关键词
- 路由(Route):路由是网关最基础也是最核心的部分,路由信息由路由ID、目标URI、一组断言和一组过滤器组成。如果断言为true则说明请求的URI和其它配置向匹配,然后进行路由转发
- 断言(Predicate):Gateway中的断言是用于开发者去定义匹配来自Http Request中的任何信息的,比如可以定义匹配请求头、参数、时间……
- 过滤器(Filter):Gateway把过滤器分为Pre Filter和Post Filter,前者在请求时进行过滤,后者在响应时进行过滤
流程图

- Gateway Client向Gateway网关发送请求
- 请求首先被HttpWebHandlerAdapter处理成功网关上下文
- 接收请求给到DispatcherHandler,它负责路由查找,并根据路由断言判断路由是否可用
- 如果路由断言通过,进入FilteringWebHandler过滤链;否则拒绝用户的访问请求
- 首先进入Pre Filter,此时会进行一些鉴权、日志打印等其它操作
- 使用负载均衡算法访问对应的微服务
- 返回响应结果,响应结果在Post Filter,期间也会进行其它操作
- 响应结果返回给用户
路由
路由规则
下面这张图是全部的路由规则:
这些规则有些用的并不是很多,下面介绍几个用的比较多的规则:Path
spring:cloud:gateway:discovery:locator:enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由routes:- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名uri: http://localhost:8001/ #匹配后提供服务的路由地址predicates:- 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. # 断言,路径相匹配的进行路由
Method
spring:
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001/ #匹配后提供服务的路由地址
predicates:
- Path=/payment/** # 断言,路径相匹配的进行路由
- Method=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”的微服务并实现负载均衡。如:

第一次路由到8001,第二次就路由到8002了
除了手动配置动态路由,还可以根据注册中心的微服务的名称进行自动动态路由,如下配置:
spring:
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
lower-case-service-id: true #是否将注册中心的服务名转小写
访问“http://localhost:9527/payment/get/12”:
404的原因其实是约定大于配置,Spring Cloud约定如果使用自动路由到注册中心的微服务的话,根URI必须是微服务名称的小写形式,如这样加上一个根路径就可以了:
过滤器
过滤器也是Gateway的核心功能,在 Gateway 的过滤器中又可以分为局部过滤器和全局过滤器。听名称就知道其作用,局部过滤器是用于某一个路由上的,而全局过滤器是用于所有路由上的。不过不管是 过滤器局部还是全局过滤器,生命周期都分为 pre 和 post,如下图所示:
- 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
SetPath
StripPrefix
PrefixPath
AddRequestParameter
自定义局部过滤器
全局过滤器
Gateway全局过滤器
全局过滤器作用于所有路由,无需配置,Gateway提供的全局过滤器已经自动生效了,已经帮我们完成了很多网关功能。下面是几个常见的Gateway全局过滤器:
| ForwardPathFilter / ForwardRoutingFilter | 路径转发相关过滤器 |
|---|---|
| LoadBalanceerClientFilter | 负载均衡客户端相关过滤器 |
| NettyRoutingFilter / NettyWriteResponseFilter | Http 客户端相关过滤器 |
| RouteToRequestUrlFilter | 路由 URL 相关过滤器 |
| WebClientHttpRoutingFilter / WebClientWriteResponseFilter | 请求 WebClient 客户端转发请求真实的URL并将响应写入到当前的请求响应中 |
| WebsocketRoutingFilter | websocket 相关过滤器 |
