内容来源:
接着上一篇文章:SpringCloud Gateway-上
简单的实战代码也基于上一篇,搭建一个简单的 SpringCloud Gateway 项目;再新启一个服务,提供一个可调用的接口即可。
网关过滤工厂
GatewayFilter Factories:允许以某种方式修改传入 http 请求或传出 http 响应
AddRequestHeader
匹配下面的路由之后,到达下一个服务会在 header 中加上 red=value 的值
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/** # path路由规则
filters:
- AddRequestHeader=red,blue
也可以使用路径添加 Header
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/{segment} # path路由规则
filters:
- AddRequestHeader=red,blue-{segment}
AddRequestParameter
给匹配的路由添加一个参数:
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/** # path路由规则
filters:
- AddRequestParameter=red,blue
同样也适用于路径匹配
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/{tt} # path路由规则
filters:
- AddRequestParameter=red,blue-{tt}
也适用于 host 匹配过滤
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- AddRequestParameter=foo, bar-{segment}
AddResponseHeader
适用范围同上,比如路径/host
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/{tt} # path路由规则
filters:
- AddResponseHeader=red,blue-{tt}
DedupeResponseHeader
跨域配置的时候防止重复,比如一般具体的服务后端会配置跨域;然后网关这边也会配置统一的跨域,那么不设置就会返回重复。
而且网关的设置会覆盖下面服务的设置:所以只会返回网关的这个设置
CircuitBreaker
短路器,用于熔断降级的。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/** # path路由规则
filters:
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/redis/myfallback
配置 5s 超时:如果服务 5s 还没响应,网关就会抛出超时
@Configuration
public class CustomizeCircuitBreakerConfig {
@Bean
public Customizer<ReactiveResilience4JCircuitBreakerFactory> defaultCustomizer() {
return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
.circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())
.timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(5)).build()).build());
}
}
如果没有指定 fallbackUri :网关会抛出自定义异常。指定了就会走指定的 uri
MapRequestHeader
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/** # path路由规则
filters:
- MapRequestHeader=Blue, X-Request-Red
当请求的时候,如果 Blue 没有传值,X-Request-Red 传值 ==》服务只有 X-Request-Red 的值
当请求的时候,如果 Blue 传值,X-Request-Red 没有传值 ==》两个都有值,只不过是一样的
当请求的时候,如果 Blue 没有传值,X-Request-Red 没有传值 ==》两个都没有值
PrefixPath
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/** # path路由规则
filters:
- PrefixPath=/mypath
访问到服务的接口会加上 「/mypath」路径
PreserveHostHeader
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/** # path路由规则
filters:
- PreserveHostHeader
决定是否发送原始主机 host ;没有参数
RequestRateLimiter
这里使用的是 Token Bucket Algorithm 「令牌桶」算法。
常见的限流算法
计数器算法
一般限制一秒钟能够通过的请求数,比如限流 qps 为100:实现思路就是从第一个请求进来开始计时,在接下来的1s内,每来一个请求,就把计数器加 1 ,累计到 100 ,那么后续请求全部被拒绝。等 1s 结束之后,把计数器恢复成 0 ,重新开始计数。
提示:AtomicLong#incrementAndGet()
弊端:如果 1s 的前 10ms 已经有100个请求,那么后面的 990ms 只能拒绝请求,这种现象:突刺现象。
漏桶算法
为了消除突刺现象,可采用漏桶算法。类似漏斗,不管上面的水流多大,下面流出的速度保持不变。相当于处理速度恒定,多余的请求会保存在桶中,在多余的就会丢弃。
提示:准备一个队列,用于保存请求,另外通过一个线程池来定期从队列中获取请求并执行,可以一次性获取多个并发执行。
弊端:无法应对短时间的突发流量。
令牌桶算法
在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择等待或者直接拒绝。
放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃,所以存在桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行「相当于可以瞬间抵挡大请求的能力,比如有 100 个令牌了,那么可以瞬间接收 10 个请求;但是漏桶算法就不行」。
提示:准备一个队列,用来保存令牌,另外通过一个线程池定期生成令牌放到队列中,每来一个请求,就从队列中获取一个令牌,并继续执行。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
限流配置:如果被限流了,返回:Status Code: 429 Too Many Requests
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/** # path路由规则
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1 #令牌桶每秒允许填充的速率
redis-rate-limiter.burstCapacity: 1 #令牌桶总容量(一秒内可执行的最大请求数)
# redis-rate-limiter.requestedTokens: 1 #一个请求需要多少令牌。这是每个请求从存储桶中获取的令牌数,默认为1.
key-resolver: "#{@pathKeyResolver}" # 使用 SpEL 表达式按名称引用 bean
redis:
host: xxx
password: xx
database: 3
@Configuration
public class KeyResolverConfiguration {
/**
* 路径限流
* @return
*/
// @Bean
// public KeyResolver pathKeyResolver()
// {
// return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
// }
/**
* 参数限流
*/
// @Bean
// KeyResolver userKeyResolver() {
// return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
// }
/**
* ip限流
* @return
*/
@Bean
public KeyResolver ipKeyResolver()
{
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
}
此时访问接口:redis 里面会出现如下的 key-value 值
request_rate_limiter.{xxx}.timestamp
request_rate_limiter.{xxx}.tokens
浏览器中测试调用,当达到临界值,会返回报错:
429 Too Many Requests
RedirectTo
重定向地址:需要两个参数:
- status:一个 300 系列的重定向 HTTP 代码
- url:有效的 URL
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/** # path路由规则
filters:
- StripPrefix=1
- RedirectTo=302,https://www.baidu.com
此时访问 http://localhost:8088/redis/gateway 会跳转到 百度
RmoveRequestHeader
移除 Header 中的值
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/** # path路由规则
filters:
- StripPrefix=1
- RmoveRequestHeader=Blue
- RemoveResponseHeader=X-Response-Foo
RemoveResponseHeader
移除返回的 header 参数
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/** # path路由规则
filters:
- StripPrefix=1
- RemoveResponseHeader=X-Response-Foo
RemoveRequestParameter
移除指定请求参数
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/** # path路由规则
filters:
- StripPrefix=1
- RemoveRequestParameter=red
RewritePath
和 - StripPrefix=1 效果一致,去掉了 redis 路径
spring:
application:
name: test-gateway
cloud:
gateway:
enabled: true # 默认配置是 true ,不需要网关则配置 false
routes:
- id: test-gateway # 指定系统id
uri: http://localhost:8080 # 系统访问地址
predicates:
- Path=/redis/** # path路由规则
filters:
- RewritePath=/redis/?(?<segment>.*), /${segment}
SaveSession
这在使用类似 Spring Session 的懒惰数据存储时特别有用,因为你需要确保在进行转发调用前已经保存了会话状态。
filters:
- SaveSession
SetRequestHeader
设置 header 参数,而不是添加
同理:
- SetResponseHeader
- SetStatus
- StripPrefix=2:去掉 URI 前面 2 个参数,表示要剥离的数量
Default Filters
添加一个过滤器,应用于所有路由
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Red, Default-Blue
- PrefixPath=/httpbin