【网关SCG】15 Spring Cloud Gateway请求匹配到多个路由如何选择?多个Predicate如何处理?

一、前言

至此微服务网关系列文章已出:

  1. 【云原生&微服务>SCG网关篇一】为什么要有网关、生产环境如何选择网关
  2. 云原生&微服务>SCG网关篇二】生产上那些灰度发布方式
  3. 【云原生&微服务>SCG网关篇三】Spring Cloud Gateway是什么、详细使用案例
  4. 云原生&微服务>SCG网关篇四】Spring Cloud Gateway内置的11种PredicateFactory如何使用
  5. 【云原生&微服务>SCG网关篇五】Spring Cloud Gateway自定义PredicateFactory
  6. 【云原生&微服务>SCG网关篇六】Spring Cloud Gateway内置的18种Filter使用姿势
  7. 【云原生&微服务>SCG网关篇七】Spring Cloud Gateway基于内置Filter实现限流、熔断、重试
  8. 【云原生&微服务>SCG网关篇八】Spring Cloud Gateway三种自定义Filter、GlobalFilter的方式
  9. 【云原生&微服务>SCG网关篇九】Spring Cloud Gateway集成Nacos详细案例
  10. 【云原生&微服务>SCG网关篇十】Spring Cloud Gateway集成Actuator、Zipkin详细案例
  11. 【云原生&微服务>SCG网关篇十一】Spring Cloud Gateway解决跨域问题
  12. 【云原生&微服务>SCG网关篇十二】Spring Cloud Gateway集成Sentinel API实现多种限流方式
  13. 源码深度剖析Spring Cloud Gateway如何处理一个请求
  14. Spring Cloud Gateway如何实现负载均衡?

聊了以下问题:

  1. 为什么要有网关?网关的作用是什么?
  2. 网关的分类?
  3. 网关的技术选型?
  4. 使用网关时常用的灰度发布方式有哪些?
  5. Spring Cloud Gateway是什么?详细使用案例?
  6. Spring Cloud Gateway内置的11种PredicateFactory
  7. 如何自定义PredicateFactory?
  8. Spring Cloud Gateway内置的18种常用的Filter
  9. Spring Cloud Gateway基于内置Filter实现限流、熔断、重试
  10. Spring Cloud Gateway三种自定义Filter、GlobalFilter的方式
  11. Spring Cloud Gateway集成Nacos案例
  12. Spring Cloud Gateway集成Actuator、Zipkin案例
  13. Spring Cloud Gareway如何解决CORS跨域问题
  14. Spring Cloud Gateway集成Sentinel API实现限流
  15. 从源码来看Spring Cloud Gateway如何处理一个请求?
  16. 从源码来看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,用&&相连;right AsyncPredicate中有1个Predicate。
  • 在进入到left.apply()方法时,left AsyncPredicate中 1个Predicate、right AsyncPredicate中 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。

猜你喜欢

转载自blog.csdn.net/Saintmm/article/details/129072283