spring cloud gateway 断言(Predicates)与过滤器(filters)

发布于:2025-05-08 ⋅ 阅读:(13) ⋅ 点赞:(0)

断言

在 Spring Cloud Gateway 中,内置的断言(Predicates)用于对请求进行条件匹配。Spring Cloud Gateway 提供了多种内置断言,可以根据请求的路径、头部、方法等条件来决定是否将请求路由到特定的处理器。

内置断言
  1. 基于路径 (Path)

匹配请求路径是否符合指定的 Ant 风格的模式。支持简单路径匹配和正则表达式匹配。

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: http://localhost:8081
          predicates:
            - Path=/users/** # 匹配所有以 /users/ 开头的路径
            - Path=/items/{id} # 匹配 /items/ 后面跟一个路径段
            - Path=/products/{category}/{id} # 匹配更复杂的路径结构
  1. 基于 HTTP 方法 (Method)

匹配请求的 HTTP 方法(GET、POST、PUT、DELETE 等)。

spring:
  cloud:
    gateway:
      routes:
        - id: product-read-service
          uri: http://localhost:8082
          predicates:
            - Path=/products/**
            - Method=GET      # 只匹配 GET 请求

        - id: product-write-service
          uri: http://localhost:8083
          predicates:
            - Path=/products/**
            - Method=POST,PUT,DELETE # 匹配 POST、PUT 或 DELETE 请求
  1. 基于请求头 (Header)

匹配请求头是否包含指定的名称和值(支持正则表达式)。

spring:
  cloud:
    gateway:
      routes:
        - id: mobile-app-service
          uri: http://localhost:8084
          predicates:
            - Path=/mobile/**
            - Header=X-Client-Type, mobile # 匹配包含 X-Client-Type: mobile 的请求

        - id: web-app-service
          uri: http://localhost:8085
          predicates:
            - Path=/web/**
            - Header=X-Client-Type, web|desktop # 匹配 X-Client-Type: web 或 X-Client-Type: desktop 的请求
            - Header=X-Version, \d+\.\d+.\d+ # 使用正则表达式匹配版本号
  1. 基于查询参数 (Query)

匹配请求是否包含指定的查询参数名称和值(支持正则表达式)。

spring:
  cloud:
    gateway:
      routes:
        - id: item-detail-service
          uri: http://localhost:8086
          predicates:
            - Path=/items/{id}
            - Query=showDetails # 匹配包含 showDetails 查询参数的请求 (值可以是任意的)

        - id: item-filter-service
          uri: http://localhost:8087
          predicates:
            - Path=/items
            - Query=category, electronics # 匹配包含 category=electronics 的查询参数
            - Query=price, \d+(\.\d+)? # 使用正则表达式匹配价格参数
  1. 基于 Host (Host)

匹配请求的 Host 请求头。

spring:
  cloud:
    gateway:
      routes:
        - id: admin-service
          uri: http://localhost:8088
          predicates:
            - Host=admin.example.com # 匹配 Host: admin.example.com

        - id: api-service
          uri: http://localhost:8089
          predicates:
            - Host=*.example.com # 匹配所有以 .example.com 结尾的 Host
  1. 基于 Cookie (Cookie)
  • 配请求是否包含指定的 Cookie 名称和值(支持正则表达式)。

    spring:
    cloud:
    gateway:
    routes:
    - id: premium-service
    uri: http://localhost:8090
    predicates:
    - Path=/premium/**
    - Cookie=userType, premium # 匹配包含 userType=premium 的 Cookie
    - Cookie=sessionId, [a-zA-Z0-9]+ # 使用正则表达式匹配 sessionId

  1. 基于日期时间 (Before, After, Between)

根据请求的时间进行匹配。

spring:
  cloud:
    gateway:
      routes:
        - id: promotion-service
          uri: http://localhost:8091
          predicates:
            - Path=/promotions/**
            - After=2025-05-07T00:00:00+09:00[Asia/Tokyo] # 在指定时间之后生效

        - id: maintenance-service
          uri: http://localhost:8092
          predicates:
            - Path=/maintenance/**
            - Before=2025-05-08T00:00:00+09:00[Asia/Tokyo] # 在指定时间之前生效

        - id: limited-offer-service
          uri: http://localhost:8093
          predicates:
            - Path=/offers/**
            - Between=2025-05-09T09:00:00+09:00[Asia/Tokyo], 2025-05-09T17:00:00+09:00[Asia/Tokyo] # 在指定时间段内生效
  1. 基于 Remote Address (RemoteAddr)

匹配请求的来源 IP 地址或 IP 地址范围。

spring:
  cloud:
    gateway:
      routes:
        - id: internal-service
          uri: http://localhost:8094
          predicates:
            - Path=/internal/**
            - RemoteAddr=192.168.1.10 # 匹配来自特定 IP 地址的请求
            - RemoteAddr=192.168.1.0/24 # 匹配来自指定 IP 地址段的请求
  1. 基于权重 (Weight)

用于实现流量分配,它使你能够在多个后端服务之间根据权重来路由请求。主要用于 A/B 测试、灰度发布、流量切分等场景,可以控制不同服务的流量分配比例。

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

权重配置由两个参数group和weight,weight按组进行计算。上面配置80% 流量转发到 weighthigh.org and ~20% 转发到 weighlow.org

组合断言

可以将多个断言组合在一起,创建更复杂的路由匹配规则。Spring Cloud Gateway 提供了逻辑运算符来进行组合:

  • and(与操作):所有条件都满足时才匹配。
  • or(或操作):只要有一个条件满足即可匹配。
    predicates:
    - Path=/get
    - (Method=GET || Method=POST)
源码阅读

在spring-cloud-gateway-server-3.1.8.jar包META-INF/spring.factories配置文件会加载GatewayAutoConfiguration自动装配类。所有的断言工厂处理类(RoutePredicateFactory)都在该配置bean中初始化,作为一个bean加载到spring容器中。
在这里插入图片描述

每个断言工厂都实现了RoutePredicateFactory接口,

配置文件中的routes会被解析到GatewayProperties配置文件中。最后封装到RouteDefinition中。

GatewayAutoConfiguration还会自动装配RouteLocator

	@Bean
	public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
			List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> predicates,
			RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) {
		return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, gatewayFilters, properties,
				configurationService);
	}

将配置文件和对应的断言工厂进行关联。

RouteDefinitionRouteLocator#initFactories()

	private void initFactories(List<RoutePredicateFactory> predicates) {
		predicates.forEach(factory -> {
			String key = factory.name();
			if (this.predicates.containsKey(key)) {
				this.logger.warn("A RoutePredicateFactory named " + key + " already exists, class: "
						+ this.predicates.get(key) + ". It will be overwritten.");
			}
          //放入hashmap中
			this.predicates.put(key, factory);
			if (logger.isInfoEnabled()) {
				logger.info("Loaded RoutePredicateFactory [" + key + "]");
			}
		});
	}

factory.name()是RoutePredicateFactory接口中默认实现

default String name() {
		return NameUtils.normalizeRoutePredicateName(getClass());
}

NameUtils#normalizeRoutePredicateName()

public static String normalizeRoutePredicateName(Class<? extends RoutePredicateFactory> clazz) {
		return removeGarbage(clazz.getSimpleName().replace(RoutePredicateFactory.class.getSimpleName(), ""));
	}

clazz.getSimpleName() 拿到当前断言工厂的类名,如PathRoutePredicateFactory就是当前类名,然后替换掉RoutePredicateFactory就变成了Path。这就和配置文件中的断言配置对应上了。最后所有的内置断言类如下:
在这里插入图片描述

处理后放到map中:

在这里插入图片描述

GatewayAutoConfiguration还会自动装配一个RouteRefreshListener

@Bean
	@ConditionalOnClass(name = "org.springframework.cloud.client.discovery.event.HeartbeatMonitor")
	public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) {
		return new RouteRefreshListener(publisher);
	}

RouteRefreshListener spring容器发布refresh事件时会调用该listener的onApplicationEvent()方法,判断是否进行reset()来发布RefreshRoutesEvent事件。CachingRouteLocator是用来处理RefreshRoutesEvent事件。

当有请求到达时,会通过RoutePredicateHandlerMapping.getHandlerInternal()方法进行路由匹配,实际在lookupRoute()方法进行逐个路由匹配

RoutePredicateHandlerMapping#lookupRoute()

protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
  //拿出所有的路由
		return this.routeLocator.getRoutes()
				// individually filter routes so that filterWhen error delaying is not a
				// problem
				.concatMap(route -> Mono.just(route).filterWhen(r -> {
					// add the current route we are testing
					exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
                  //调用每个PredicateFactory的apply方法进行路由匹配
					return r.getPredicate().apply(exchange);
				})
						// instead of immediately stopping main flux due to error, log and
						// swallow it
						.doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e))
						.onErrorResume(e -> Mono.empty()))

				.next()
				.map(route -> {
					validateRoute(route, exchange);
					return route;
				});

	}

过滤器

网关过滤器允许请求被路由到后端服务之前或之后对请求和响应进行修改和处理。过滤器可以执行各种任务,例如添加/修改请求头和响应头、记录日志、进行身份验证和授权、限流、熔断、URL 重写等等。路由过滤器是针对单个路由进行配置的,只对匹配该路由的请求生效。在配置文件中配置在具体某个路由节点的下面。还有一种是全局过滤器(GlobalFilter )对所有的路由生效。spring cloud gateway内置了很多过滤器。

1、请求响应头修改类

添加请求和响应头

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-ID, ${random.uuid()}
  		- AddResponseHeader=X-Response-By, Gateway

删除header

filters:
  - RemoveRequestHeader=Cookie
  - RemoveResponseHeader=X-Response-Foo

2、路径处理类

PrefixPath: 在请求路径前添加指定的前缀。

filters:
  - PrefixPath=/backend

对当前路由所有匹配的请求添加/backend前缀,如/hello 会向后转发/backend/hello

StripPrefix: 从请求路径中剥离指定数量的前缀。

      routes:
      - id: nameRoot
        uri: https://nameservice
        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2

StripPrefix 过滤器采用一個数量参数。指示在向下游发送請求之前要從路徑中剥离的部分數量。

如上面的配置,原始请求 /name/one/two 会转到 https://nameservice/two 。会舍弃请求路径前两个部分。

RewritePath: 使用正则表达式重写请求路径。

filters:
  - RewritePath=/api/(?<segment>.*), /$\\{segment}

RedirectTo: 将请求重定向到另一个 URL。

filters:
  - RedirectTo=302, https://new.example.com/${segment}

还有一些RequestRateLimiter请求限流、Retry请求重试、MapRequestHeader请求头映射等等。这些过滤器和一些web 中间件像nginx功能类似。

全局过滤器

全局过滤器不针对特定的路由配置,而是会拦截并处理所有通过 Gateway 的 HTTP 请求和响应。这意味着无论请求匹配到哪个路由,全局过滤器都会被执行(除非有特殊的逻辑在过滤器内部进行排除)。全局过滤器可以进行鉴权、日志记录、跨域处理等公共处理。

Spring Cloud Gateway 提供了一些内置的全局过滤器(继承自 GlobalFilter),例如:

过滤器类 功能描述
NettyRoutingFilter 将请求路由到实际服务(核心路由逻辑)
NettyWriteResponseFilter 处理响应内容写回客户端
RouteToRequestUrlFilter 将路由转换为具体请求 URL
ForwardPathFilter 用于内部转发请求
GatewayMetricsFilter 记录路由指标数据,供监控使用

这些内置过滤器通过不同的 Order 值按顺序执行,控制整个网关生命周期的处理流程。

内置全局过滤器的初始化也是在GatewayAutoConfiguration自动装配中完成的,如RouteToRequestUrlFilter过滤器

	@Bean
	@ConditionalOnEnabledGlobalFilter
	public RouteToRequestUrlFilter routeToRequestUrlFilter() {
		return new RouteToRequestUrlFilter();
	}

@ConditionalOnEnabledGlobalFilter注解会根据配置文件的enable值进行条件装配。其基类是OnEnabledComponent。配置项前缀是"spring.cloud.gateway.“,后缀是”.enabled"。ConditionalOnEnabledGlobalFilter上又有条件注解@Conditional(OnEnabledGlobalFilter.class),会在前面前后缀基础上中间添加:“global-filter.”+全局过滤器类名去掉GlobalFilter后剩下类名驼峰转”-“连接。

那ForwardPathFilter的完整对应配置项就是:spring.cloud.gateway.global-filter.forward-path.enabled。

spring:
  cloud:
    gateway:
      global-filter:
        forward-path:
          enabled: true

自定义一个全局过滤器也很简单,实现GlobalFilter 接口。并可选实现 org.springframework.core.Ordered 接口指定执行顺序。将该类作为一个bean放到spring容器中即可。

过滤器的触发是在FilteringWebHandler类中完成,FilteringWebHandler也是一个自动装配的bean。

FilteringWebHandler#handle()

	public Mono<Void> handle(ServerWebExchange exchange) {
		Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
		List<GatewayFilter> gatewayFilters = route.getFilters();
		//全局过滤器和路由过滤器合并,
		List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
		combined.addAll(gatewayFilters);
		//按order进行排序
		AnnotationAwareOrderComparator.sort(combined);

		//构造过滤器链进行依次执行	
		return new DefaultGatewayFilterChain(combined).filter(exchange);
	}

网站公告

今日签到

点亮在社区的每一天
去签到