本文我们在前文的基础上,开始讲gateway过滤器的部分内容。gateway的过滤器分为内置过滤器Filter和全局过滤器GlobalFilter。本章节先讲内置过滤器Filter。
需要先声明一下内置过滤器和全局过滤器之间的区别:
通常情况下,会根据路由的具体需求选择并配置不同的内置过滤器来完成特定的功能。如果有一些通用的过滤逻辑需要应用到所有的请求上,可以选择使用全局过滤器来实现。
内置过滤器其实挺多的,这里挑一些常用的说一下:
StripPrefix
:去除请求的前缀。RewritePath
:重写请求的路径。RateLimiter
:限流过滤器,用于控制请求的速率。Retry
:对请求进行重试。AddRequestHeader
:用于添加请求头。AddResponseHeader
:在响应头中添加指定的键值对。SetRequestHeader
:设置请求头的值。SetResponseHeader
:设置响应头的值。AddRequestParameter
:添加请求参数。RemoveRequestHeader
:移除请求头。RemoveResponseHeader
:移除响应头。Hystrix
:将请求包裹在Hystrix断路器中,提供服务容错能力。spring:
cloud:
gateway:
routes:
- id: system-api
uri: lb://system-api
predicates:
- Path=/system-api/**
filters:
- StripPrefix=1
这个是最常用的过滤器,作用是过滤掉url中前n
节,比如我们发送请求:
localhost:8080/system-api/system/user/info
此时因为我们配置了StripPrefix=1
,实际请求的地址就变成:
localhost:8080/system/user/info
spring:
cloud:
gateway:
routes:
- id: system-api
uri: lb://system-api
predicates:
- Path=/system-api/**
filters:
- StripPrefix=1
- RewritePath=/system/(?>.*), /$\{segment}
具体的重写规则为将 “/system/xxx” 重写为 “/xxx”,其中 xxx 是根据正则表达式捕获的片段(segment)。
比如发起请求:
localhost:8080/system-api/system/user/info
那么该请求就会请求到:
localhost:8080/user/info
spring:
cloud:
gateway:
routes:
- id: system-api
uri: lb://system-api
predicates:
- Path=/system-api/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
key-resolver: "#{@remoteAddrKeyResolver}" # 使用 SpEL 表达式按名称引用 bean
redis-rate-limiter.replenishRate: 10 # 令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 20 # 令牌桶总容量
在上面的示例中,我们使用了 name: RequestRateLimiter
来指定使用 RateLimiter 过滤器,并通过 args
属性来设置 RateLimiter 过滤器的参数。
在这个示例中,我们设置了以下参数:
key-resolver: "#{@remoteAddrKeyResolver}"
:用于指定从请求中提取限流键的解析器。你可以自定义一个实现了 KeyResolver
接口的 Bean,并在此处引用它。redis-rate-limiter.replenishRate: 10
:限流桶每秒填充的令牌数为 10。redis-rate-limiter.burstCapacity: 20
:限流桶的容量为 20。由于这个限流过滤器是使用Redis
进行实现的,所以我们需要引入对应的Redis
依赖,并配置对应的Redis
配置。
引入依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redis-reactiveartifactId>
dependency>
添加Redis配置:
spring:
redis:
host: localhost # Redis 主机名
port: 6379 # Redis 端口号
password: 123456 # Redis 密码(如果有的话)
timeout: 2000 # 连接超时时间(毫秒)
对应的KeyResolver配置类:
@Configuration
public class KeyResolverConfiguration {
@Primary
@Bean
public KeyResolver remoteAddrKeyResolver() {
return exchange -> Mono
.just(Objects.requireNonNull(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()))
.getAddress().getHostAddress());
}
@Bean
public KeyResolver pathKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
}
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getURI().getHost());
}
}
这样,我们启动应用,进行压力测试:
这里我们可以看到,我们进行了5
秒压测,由于我们的配置:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
所以令牌桶中初始有20个令牌,而每秒钟10个令牌的速率重新填充。我们压测进行了5秒,因此结果有20+5*10=70
个请求请求成功,理论与我们的压测结果达到了一致。
spring:
cloud:
gateway:
routes:
- id: system-api
uri: lb://system-api
predicates:
- Path=/system-api/**
filters:
- StripPrefix=1
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY,INTERNAL_SERVER_ERROR
methods: GET,POST
series: SERVER_ERROR
如上图示例,我们使用了 name: Retry
来指定使用 Retry 过滤器,并通过 args
属性来设置 Retry 过滤器的参数。在这个示例中,我们设置了以下参数:
retries: 3
:最大重试次数为 3。statuses: BAD_GATEWAY,INTERNAL_SERVER_ERROR
:当响应状态码为 BAD_GATEWAY
或 INTERNAL_SERVER_ERROR
时触发重试。取值参考:org.springframework.http.HttpStatusmethods: GET,POST
:只对 GET 和 POST 请求方法进行重试。取值参考:org.springframework.http.HttpMethodseries: SERVER_ERROR
:只对服务器错误类别的响应进行重试(即状态码以 5 开头的响应)。org.springframework.http.HttpStatus.Seriesspring:
cloud:
gateway:
routes:
- id: system-api
uri: lb://system-api
predicates:
- Path=/system-api/**
filters:
- StripPrefix=1
- AddRequestHeader=X-Example-Header, example-value
在上述示例中,AddRequestHeader
过滤器将在请求的头部中添加一个名为X-Example-Header
的字段,并设置其值为example-value
。通过这种方式,当请求被转发到目标服务时,就会携带额外的请求头信息。后续的服务可以读取这些请求头并进行相应的处理。
spring:
cloud:
gateway:
routes:
- id: system-api
uri: lb://system-api
predicates:
- Path=/system-api/**
filters:
- StripPrefix=1
- AddResponseHeader=X-Example-Header, example-value
在上述示例中,AddResponseHeader
过滤器会在响应的头部中添加一个名为X-Example-Header
的字段,并设置其值为example-value
。通过这种方式,目标服务返回的响应会携带额外的响应头信息。
spring:
cloud:
gateway:
routes:
- id: system-api
uri: lb://system-api
predicates:
- Path=/system-api/**
filters:
- StripPrefix=1
- SetRequestHeader=X-Custom-Header, custom-value
在上述示例中,当匹配成功时,会使用 SetRequestHeader
过滤器来设置请求头X-Custom-Header
,并指定其值为 custom-value
。
spring:
cloud:
gateway:
routes:
- id: system-api
uri: lb://system-api
predicates:
- Path=/system-api/**
filters:
- StripPrefix=1
- SetResponseHeader=X-Custom-Header, custom-value
在上述示例中,当匹配成功时,会使用 SetResponseHeader
过滤器来设置响应头X-Custom-Header
,并指定其值为 custom-value
。
spring:
cloud:
gateway:
routes:
- id: system-api
uri: lb://system-api
predicates:
- Path=/system-api/**
filters:
- StripPrefix=1
- AddRequestParameter=apiKey,my-value
在上面的例子中,我们使用AddRequestParameter
过滤器并提供了两个参数:
apiKey
:要添加的请求参数的名称。my-value
:要添加的请求参数的值。通过这个配置,当请求匹配时,将会自动添加一个名为apiKey
,值为my-value
的请求参数。
spring:
cloud:
gateway:
routes:
- id: system-api
uri: lb://system-api
predicates:
- Path=/system-api/**
filters:
- StripPrefix=1
- name: RemoveRequestHeader
args:
name: X-Auth-Token
上面示例中,我们使用了 name: RemoveRequestHeader
来指定使用 RemoveRequestHeader 过滤器。通过 args
属性来设置 RemoveRequestHeader 过滤器的参数。
在这个示例中,我们设置了以下参数:
name: X-Auth-Token
:要移除的请求头的名称。这样配置之后,当请求匹配时,将会自动移除请求中的 X-Auth-Token
请求头,并将修改后的请求继续传递给对应服务。
spring:
cloud:
gateway:
routes:
- id: system-api
uri: lb://system-api
predicates:
- Path=/system-api/**
filters:
- StripPrefix=1
- name: RemoveResponseHeader
args:
name: X-Custom-Header
上面例子中,我们使用了 name: RemoveResponseHeader
来指定使用 RemoveResponseHeader 过滤器。通过 args
属性来设置 RemoveResponseHeader 过滤器的参数。
在这个示例中,我们设置了以下参数:
name: X-Custom-Header
:要移除的响应头的名称。这样配置之后,当请求匹配时,将会自动移除响应中的 X-Custom-Header
响应头,并将修改后的响应返回给客户端。
这部分有点特殊,在3.1.8版本的spring-cloud-starter-gateway
中,Hystrix
这个Filter
已经被正式移除了,这之后更加倾向于单独使用Hystrix
而不是在gateway
中的Hystrix
。
如果你想要按照:
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
这样的形式尝试使用Hystrix,那么就会报错Unable to find GatewayFilterFactory with name hystrix
另外使用Hystrix
我们后面再讲。