文章目录
- 一、前言
- 二、RoutePredicateHandlerMapping匹配路由
- 1、Gateway处理请求的流程
- 2、RoutePredicateHandlerMapping匹配路由
- 1)获取所有路由
- 2)多个Predicate谓词匹配
- 3)Flux.next()
- 4)校验路由
- 5)返回结果
- 三、总结
一、前言
至此微服务网关系列文章已出:
- 【云原生&微服务>SCG网关篇一】为什么要有网关、生产环境如何选择网关
- 云原生&微服务>SCG网关篇二】生产上那些灰度发布方式
- 【云原生&微服务>SCG网关篇三】Spring Cloud Gateway是什么、详细使用案例
- 云原生&微服务>SCG网关篇四】Spring Cloud Gateway内置的11种PredicateFactory如何使用
- 【云原生&微服务>SCG网关篇五】Spring Cloud Gateway自定义PredicateFactory
- 【云原生&微服务>SCG网关篇六】Spring Cloud Gateway内置的18种Filter使用姿势
- 【云原生&微服务>SCG网关篇七】Spring Cloud Gateway基于内置Filter实现限流、熔断、重试
- 【云原生&微服务>SCG网关篇八】Spring Cloud Gateway三种自定义Filter、GlobalFilter的方式
- 【云原生&微服务>SCG网关篇九】Spring Cloud Gateway集成Nacos详细案例
- 【云原生&微服务>SCG网关篇十】Spring Cloud Gateway集成Actuator、Zipkin详细案例
- 【云原生&微服务>SCG网关篇十一】Spring Cloud Gateway解决跨域问题
- 【云原生&微服务>SCG网关篇十二】Spring Cloud Gateway集成Sentinel API实现多种限流方式
- 源码深度剖析Spring Cloud Gateway如何处理一个请求
- Spring Cloud Gateway如何实现负载均衡?
聊了以下问题:
- 为什么要有网关?网关的作用是什么?
- 网关的分类?
- 网关的技术选型?
- 使用网关时常用的灰度发布方式有哪些?
- Spring Cloud Gateway是什么?详细使用案例?
- Spring Cloud Gateway内置的11种PredicateFactory
- 如何自定义PredicateFactory?
- Spring Cloud Gateway内置的18种常用的Filter
- Spring Cloud Gateway基于内置Filter实现限流、熔断、重试
- Spring Cloud Gateway三种自定义Filter、GlobalFilter的方式
- Spring Cloud Gateway集成Nacos案例
- Spring Cloud Gateway集成Actuator、Zipkin案例
- Spring Cloud Gareway如何解决CORS跨域问题
- Spring Cloud Gateway集成Sentinel API实现限流
- 从源码来看Spring Cloud Gateway如何处理一个请求?
- 从源码来看Spring Cloud Gateway如何实现负载均衡?
本文就基于源码深度剖析Spring Cloud Gateway如何处理一个请求的请求执行流程,继续来看如果请求匹配到多个路由怎么处理?
二、RoutePredicateHandlerMapping匹配路由
1、Gateway处理请求的流程
Spring Cloud Gateway对请求的处理流程见博文:源码深度剖析Spring Cloud Gateway如何处理一个请求;
RoutePredicateHandlerMapping这个HandlerMapping会负责对请求进行路由匹配。
2、RoutePredicateHandlerMapping匹配路由
RoutePredicateHandlerMapping#lookupRoute(ServerWebExchange)方法会从我们application.yml文件中配置的所有路由中根据Predicate做路由匹配,找到当前请求对应的路由。
以如下application.yml配置文件中配置的路由信息为例:
spring:
cloud:
gateway:
routes:
- id: gateway-nacos-service-route-fail
uri: lb://gateway-nacos-provider
predicates:
- Path=/nacos/**
- Before=2024-01-01T00:00:00.000+08:00[Asia/Shanghai]
- After=2023-01-01T00:00:00.000+08:00[Asia/Shanghai]
filters:
- PrefixPath=/saint
- id: gateway-nacos-service-route
uri: lb://gateway-nacos-provider
predicates:
- Path=/nacos/**
filters:
- StripPrefix=1
针对http://localhost:9999/nacos/
路径过来的请求都会匹配到两个路由:gateway-nacos-service-route-fail、gateway-nacos-service-route。
1)获取所有路由
实例application.yml文件中一共给出了两个路由,所以lookupRoute()方法中也一共找到两个自定的路由。
获取到所有的路由之后,会对路由进行Predicate谓词匹配。即:进入到Route.getPredicate().apply方法。
2)多个Predicate谓词匹配
在源码中判断Route是否有效时,仅仅是通过route.getPredicate()方法获取到一个Predicate;
public AsyncPredicate<ServerWebExchange> getPredicate() {
return this.predicate;
}
但是一个Route中可能包含多个Predicate;
Gateway使用AsyncPredicate接口的实现类AndAsyncPredicate
来包装所有的Predicate;AndAsyncPredicate
将所有的Predicate分为两部分:left 和 right,而每一部分的Predicate又都被AsyncPredicate
包装;
就路由gateway-nacos-service-route-fail而言:
- 其中有三个Predicate,分别为:Path、Before、After;
- 最上层的AndAsyncPredicate中的left 为Path 和 Before 组合在一起的 AndAsyncPredicate(其中left为Path,right为Before)、right为After。
调用Predicate.apply()方法做谓词匹配时,会分别调用left 和 right 的apply()方法;
示例中的Route中有三个Predicate:
- 第一次调用predicate.apply()方法时,left
AsyncPredicate
中有2个Predicate,用&&相连;rightAsyncPredicate
中有1个Predicate。- 在进入到left.apply()方法时,left
AsyncPredicate
中 1个Predicate、rightAsyncPredicate
中 1个Predicate;- 这里可以看做是针对left#apply()方法的一个递归操作,直到left中仅包含一个Predicate时,再往上返回。
也就是说,无论Route中包含多少个Predicate,包装后的 AsyncPredicate
中 right Predicate中仅包含一个Predicate,其余的都是在left Predicate中(并且left的Predicate使用AndAsyncPredicate)。
3)Flux.next()
Flux.next()表示将 通量发出的第一项发射到新的单声道(Mono)中,即:仅会取第一个匹配的Route,第二个匹配的路由甚至都不会做Predicate谓词匹配。
大家可以按我的断点自己去调试一下看看效果。
4)校验路由
RoutePredicateHandlerMapping#validateRoute()方法负责校验谓词匹配到的路由,但是默认validateRoute()方法并没有实现;
开发人员可以在子类中重写它,比如:强制执行URL映射中表示的特定前提条件
5)返回结果
因为网关后面的微服务中没有/saint/nacos/
路径的HTTP接口,所以网关层面选中gateway-nacos-service-route-fail
之后,即使Filter执行链出错了,也不会继续返回重新找下一个Predicate谓词匹配的Route。
三、总结
按照application.yml文件中的定义route的顺序从上到下依次匹配(无论匹配到的Route的Filter是否可以通过、访问的地址是否有效,都不会继续往下匹配)
无论Route中包含多少个Predicate,包装后的 AsyncPredicate
中 right Predicate中仅包含一个Predicate,其余的都是在left Predicate中;
- left的Predicate使用AndAsyncPredicate,再对其中的Predicate用left、right递归拆分,直到left中仅有一个Predicate。