SpringCloud——网关过滤工厂GatewayFilterFactory

GatewayFilter 工厂

网关过滤器工厂GatewayFilterFactory 允许以某种方式修改传入的HTTP 请求或返回的HTTP 响应。其作用域是某些特定路由。SpringCloudGateway 包括很多种内置的网关过滤器工厂。下面会学习较常用的几种。

AddRequestHeader

AddRequestHeader GatewayFilter 工厂需要一个 name 和 value 参数。下面的例子配置了一个 AddRequestHeader GatewayFilter

添加请求头

spring:
  application:
    name: depart-consumer # 微服务名称
  cloud:
    gateway:
      routes:
        - id: add_request_header_route
          uri: http://localhost:8080
          predicates:
            - Path=/**
          filters:
            - AddRequestHeader=X-Request-Color, blue

新建一个服务,端口8080,获取请求头

package com.zjw.controller;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Enumeration;

/**
 * @since 2023/11/28 13:41
 */
@RestController
@RequestMapping("/info")
public class ShowInfoController {

    @GetMapping("/header")
    public String getHeader(HttpServletRequest request){
        Enumeration<String> headers = request.getHeaders("X-Request-Color");
        StringBuilder sb = new StringBuilder();
        while (headers.hasMoreElements()) {
            sb.append(headers.nextElement()).append(" ");
        }
        return "X-Request-Color: " + sb.toString();
    }
}

通过api实现增加请求头。

@Configuration
public class GatewayConfig {

    /**
     * 增加请求头
     */
    @Bean
    public RouteLocator addRequestHeader(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("add_request_header_route",
                        r -> r.path("/**")
                                .filters(fs -> fs
                                        .addRequestHeader("X-Request-Color", "blue")
                                        .addRequestHeader("X-Request-Color", "red"))
                                .uri("http://localhost:8080"))
                .build();
    }
}

AddRequestHeadersIfNotPresent

AddRequestHeadersIfNotPresent GatewayFilter 工厂接受一个由冒号分隔的 name 和 value 键值对的集合。下面的例子配置了一个 AddRequestHeadersIfNotPresent GatewayFilter

spring:
  application:
    name: depart-consumer # 微服务名称
  cloud:
    gateway:
      routes:
        - id: add_request_header_route
          uri: http://localhost:8080
          predicates:
            - Path=/**
          filters:
            - AddRequestHeader=X-Request-Color, blue
            - AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-2:green

这个列表为所有匹配的请求在下游请求的header信息中添加了两个header信息 X-Request-Color-1:blueX-Request-Color-2:green。这类似于 AddRequestHeader 的工作方式,但与 AddRequestHeader 不同的是,它只在header 信息不存在的情况下才会这样做。否则,客户端请求中的原始值将被发送。

java写法

@Configuration
public class GatewayConfig {

    /**
     * addRequestHeader增加请求头
     */
    @Bean
    public RouteLocator addRequestHeadersIfNotPresent(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("add_request_header_route",
                        r -> r.path("/**")
                                .filters(fs -> fs
                                        .addRequestHeadersIfNotPresent("X-Request-Color:blue"))
                                .uri("http://localhost:8080"))
                .build();
    }
}

AddRequestParameter

AddRequestParameter GatewayFilter Factory需要一个 name 和 value 参数。下面的例子配置了一个 AddRequestParameter GatewayFilter

spring:
  application:
    name: depart-consumer # 微服务名称
  cloud:
    gateway:
      routes:
        - id: add_request_parameter_route
          uri: http://localhost:8080
          predicates:
            - Path=/**
          filters:
            - AddRequestParameter=color, blue
            - AddRequestParameter=color, red

java实现

@Configuration
public class GatewayConfig {

    /**
     * addRequestHeader增加请求头
     */
    @Bean
    public RouteLocator addRequestParameter(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("add_request_parameter_route",
                        r -> r.path("/**")
                                .filters(fs -> fs
                                        .addRequestParameter("color","red"))
                                .uri("http://localhost:8080"))
                .build();
    }
}

AddResponseHeader

AddResponseHeader GatewayFilter 工厂需要一个 name 和 value 参数。下面的例子配置了一个 AddResponseHeader GatewayFilter。

spring:
  application:
    name: depart-consumer # 微服务名称
  cloud:
    gateway:
      routes:
        - id: add_response_header_route
          uri: http://localhost:8080
          predicates:
            - Path=/**
          filters:
            - AddResponseHeader=X-Response-Color, Blue

java实现

@Configuration
public class GatewayConfig {

    /**
     * addResponseHeader增加响应头
     */
    @Bean
    public RouteLocator addResponseHeader(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("add_response_header_route",
                        r -> r.path("/**")
                                .filters(fs -> fs
                                        .addResponseHeader("color","red"))
                                .uri("http://localhost:8080"))
                .build();
    }
}

CircuitBreaker

断路器,在微服务架构和Spring Cloud中,断路器模式用于防止故障在分布式系统中蔓延,通过在服务出现问题时自动“断开”服务,从而保护系统的其他部分不受影响。

要启用 Spring Cloud CircuitBreaker 过滤器,你需要添加 spring-cloud-starter-circuitbreaker-reactor-resilience4j 依赖。

spring:
  application:
    name: depart-consumer # 微服务名称
  cloud:
    gateway:
      routes:
        - id: circuitbreaker_route
          uri: http://localhost:8080
          predicates:
            - Path=/**
          filters:
            - name: CircuitBreaker
              args:
                name: myCircuitBreaker
                fallbackUri: forward:/fb

controller

@RestController
public class FallbackController {

    @GetMapping("/fb")
    public String fallback(){
        return "服务暂不可用";
    }
}

java实现

@Configuration
public class GatewayConfig {

    /**
     * addResponseHeader增加响应头
     */
    @Bean
    public RouteLocator circuitBreaker(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("circuitbreaker_route",
                        r -> r.path("/**")
                                .filters(fs -> fs
                                        .circuitBreaker(config -> {
                                            config.setName("CircuitBreaker");
                                            config.setFallbackUri("forward:/fb");
                                        }))
                                .uri("http://localhost:8080"))
                .build();
    }
}

当访问的服务出现问题时,会调用配置的CircuitBreaker断路器。

PrefixPath

PrefixPath GatewayFilter 工厂需要一个 prefix 参数。下面的例子配置了一个 PrefixPath GatewayFilter

spring:
  application:
    name: depart-consumer # 微服务名称
  cloud:
    gateway:
      routes:
        - id: prefixpath_route
          uri: http://localhost:8080
          predicates:
            - Path=/**
          filters:
            - PrefixPath=/info

这就把 /info 作为所有匹配请求的路径的前缀。因此,一个到 /hello 的请求会被发送到 /info/hello

java实现

@Configuration
public class GatewayConfig {

    /**
     * prefixPath增加路径前缀
     */
    @Bean
    public RouteLocator prefixpathRoute(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("prefixpathRoute", r -> r.path("/**")
                        .filters(gatewayFilterSpec -> gatewayFilterSpec
                                .prefixPath("/info"))
                        .uri("http://localhost:8080"))
                .build();
    }
}

StripPrefix

StripPrefix GatewayFilter 工厂需要一个参数,即 partsparts 参数表示在向下游发送请求之前要从路径中剥离的部分的数量。下面的列表配置了一个 StripPrefix GatewayFilter

spring:
  application:
    name: depart-consumer # 微服务名称
  cloud:
    gateway:
      routes:
        - id: nameRoot
          uri: http://localhost:8080
          predicates:
            - Path=/**
          filters:
            - StripPrefix=2

当通过网关向 /a/b/info 发出请求时,向localhost:8080发出的请求看起来像 localhost:8080/info

java实现

@Configuration
public class GatewayConfig {

    /**
     * stripPrefix去掉路径前缀
     */
    @Bean
    public RouteLocator stripPrefixRoute(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("stripPrefixRoute", r -> r.path("/**")
                        .filters(gatewayFilterSpec -> gatewayFilterSpec
                                .stripPrefix(2))
                        .uri("http://localhost:8080"))
                .build();
    }
}

RewritePath

RewritePath GatewayFilter 工厂接收一个路径 regexp 参数和一个 replacement 参数。这是用Java正则表达式来重写请求路径的一种灵活方式。下面的列表配置了一个 RewritePath GatewayFilter

spring:
  application:
    name: depart-consumer # 微服务名称
  cloud:
    gateway:
      routes:
        - id: rewritepath_route
          uri: http://localhost:8080
          predicates:
            - Path=/a/**
          filters:
            - RewritePath=/a, /info

对于请求路径为 /a/header 的情况,在进行下游请求之前将路径设置为 /info/header

java实现

@Configuration
public class GatewayConfig {

    /**
     * rewritePath路径替换
     */
    @Bean
    public RouteLocator rewritePathRoute(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("stripPrefixRoute", r -> r.path("/**")
                        .filters(gatewayFilterSpec -> gatewayFilterSpec
                                .rewritePath("/a", "/info"))
                        .uri("http://localhost:8080"))
                .build();
    }
}

Redis RateLimiter

Redis的实现是基于 Stripe 的工作。它需要使用 spring-boot-starter-data-redis-reactive Spring Boot Starter。

使用的算法是 令牌桶算法

redis-rate-limiter.replenishRate 属性定义了每秒钟允许多少个请求(不算放弃的请求)。这是令牌桶被填充的速度。

redis-rate-limiter.burstCapacity 属性是一个用户在一秒钟内允许的最大请求数(不算放弃的请求)。这是令牌桶可以容纳的令牌数量。将此值设置为零会阻止所有请求。

redis-rate-limiter.requestedTokens 属性是指一个请求要花费多少令牌。这是为每个请求从桶中提取的令牌数量,默认为 1。

spring:
  application:
    name: depart-consumer # 微服务名称
  data:
    redis:
      host: redis
      port: 6379
  cloud:
    gateway:
      routes:
        - id: requestratelimiter_route
          uri: http://localhost:8080
          predicates:
            - Path=/**
          filters:
            - name: RequestRateLimiter
              args:
                key-resolver: "#{@ipKeyResolver}"
                redis-rate-limiter.replenishRate: 2
                redis-rate-limiter.burstCapacity: 5
                redis-rate-limiter.requestedTokens: 1

java中配置KeyResolver

package com.zjw.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;

/**
 * @since 2023/11/28 20:27
 */
@Configuration
public class SpringCloudConfig {

    /**
     * ip限流
     */
    @Bean
    public KeyResolver ipKeyResolver(){
        return exchange -> {
            String hostName = exchange.getRequest().getRemoteAddress().getHostName();
            System.out.println("hostName = " + hostName);
            return Mono.just(hostName);
        };
    }
}

如果请求的速率过快,会出现429错误。

Default Filters

在 Spring Cloud Gateway 中,"Default Filters" 指的是那些在网关中默认应用的一组过滤器。Spring Cloud Gateway 是基于 Spring Framework 构建的一个 API 网关,它提供了路由、过滤和转发请求到微服务的功能。Default Filters 是这个网关提供的一些内置的过滤器,它们被用来处理进出网关的请求和响应。这些过滤器的目的是提供一些常用的功能,比如修改请求或响应的头部、添加日志、限流等。

Default Filters 包括但不限于:

  1. AddRequestHeader Filter: 向请求中添加一个新的头部。
  2. AddResponseHeader Filter: 向响应中添加一个新的头部。
  3. RewritePath Filter: 重写请求路径。
  4. SetPath Filter: 设置请求的路径。
  5. SetStatus Filter: 设置响应的状态码。
  6. Retry Filter: 在失败的情况下重试请求。
  7. RateLimiter Filter: 限制请求的速率。

要添加一个filter并将其应用于所有路由,可以使用 spring.cloud.gateway.default-filters。这个属性需要一个filter的列表。

spring:
  application:
    name: depart-consumer # 微服务名称
  cloud:
    gateway:
      default-filters:
        - name: CircuitBreaker
          args:
            name: myCircuitBreaker
            fallbackUri: forward:/fb

这个过滤器的作用是在微服务调用中提供断路器功能,以确保系统的稳定性和弹性

优先级

对于相同filter工厂,在不同位置设置了不同的值,则优先级为:

  • 局部filter的优先级高于默认filter的
  • API式的filter优先级高于配置式filter的

自定义过滤器工厂

添加自定义类

package com.zjw.factory;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

/**
 * 自定义过滤器工厂
 * @since 2023/11/28 21:39
 */
@Component
public class AddHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {

    /**
     * 修改请求头,使用方式:
     * 在配置中添加
     * filters:
     *  - AddHeader=new-color, red
     */
    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return (exchange, chain) -> {
            ServerHttpRequest httpRequest = exchange.getRequest()
                                                    .mutate()
                                                    .header(config.getName(), config.getValue())
                                                    .build();
            ServerWebExchange webExchange = exchange.mutate()
                    .request(httpRequest)
                    .build();

           return chain.filter(webExchange);
        };
    }
}

配置yml

spring:
  application:
    name: depart-consumer # 微服务名称
  cloud:
    gateway:
      routes:
        - id: customize_filter
          uri: http://localhost:8080
          predicates:
            - Path=/**
          filters:
            - AddHeader=new-color, red
posted @ 2023-11-28 15:33  雨中遐想  阅读(274)  评论(0编辑  收藏  举报