网关 Spring Cloud Gateway 内置网关过滤器工厂总结


💨 作者:laker,因为喜欢LOL滴神faker,又是NBA湖人队🏀(laker)粉丝儿(主要是老詹的粉丝儿),本人又姓,故取笔名:laker
❤️喜欢分享自己工作中遇到的问题和解决方案以及一些读书笔记和心得分享
🌰本人创建了微信公众号【Java大厂面试官】,用于和大家交流分享
🏰 个人微信【lakernote】,加作者备注下暗号:cv之道


本文Spring Cloud Gateway 版本:2020.0.0

1.简介

Spring Cloud Gateway是微服务中经常使用的智能代理服务。它透明地将请求集中在单个入口点中,并将其路由到适当的服务。它的一个最有趣的特点是概念过滤器网页过滤GatewayFilter)。

WebFilter与谓词工厂一起,包含完整的路由机制。Spring Cloud Gateway提供了许多内置的WebFilter工厂,这些工厂允许在到达代理服务之前与HTTP请求进行交互,并在将结果交付给客户端之前与HTTP响应进行交互。也可以实现自定义过滤器。

2. WebFilter工厂

网页过滤(或GatewayFilter)工厂允许修改所述入站HTTP请求和出站HTTP响应。从这个意义上讲,它提供了一组有趣的功能,可以在与下游服务交互之前和之后应用。

Spring Cloud Gateway WebFilter工厂架构

处理程序映射管理客户端的请求。它检查它是否与某些已配置的路由匹配。然后,它将请求发送到Web处理程序以执行此路由的特定筛选器链。虚线在前置和后置滤波器逻辑之间划分逻辑。收入过滤器在代理请求之前运行。输出过滤器收到代理响应后便开始起作用。筛选器提供了在两者之间修改过程的机制。

3.实施WebFilter工厂

让我们回顾一下Spring Cloud Gateway项目中包含的最重要的WebFilter工厂。有两种方法可以实现它们,即使用YAML或Java DSL。我们将展示如何实现这两个示例。

3.1 HTTP请求

内置的WebFilter工厂允许与HTTP请求的标头和参数进行交互。我们可以添加 (AddRequestHeader), 映射 (MapRequestHeader)设置或替换 (SetRequestHeader), 以及删除 *(RemoveRequestHeader)*标头值并将其发送到代理服务。原始主机标头也可以保留(PreserveHostHeader)。

同样,我们可以添加 *(AddRequestParameter)*和删除 *(RemoveRequestParameter)*参数,以供下游服务处理。让我们看看如何做到这一点:

- id: add_request_header_route
  uri: https://httpbin.org
  predicates:
  - Path=/get/**
  filters:
  - AddRequestHeader=My-Header-Good,Good
  - AddRequestHeader=My-Header-Remove,Remove
  - AddRequestParameter=var, good
  - AddRequestParameter=var2, remove
  - MapRequestHeader=My-Header-Good, My-Header-Bad
  - MapRequestHeader=My-Header-Set, My-Header-Bad
  - SetRequestHeader=My-Header-Set, Set 
  - RemoveRequestHeader=My-Header-Remove
  - RemoveRequestParameter=var2

让我们检查一切是否按预期进行。为此,我们将使用curl和公开可用的httpbin.org

$ curl http://localhost:8080/get
{
  "args": {
    "var": "good"
  },
  "headers": {
    "Host": "localhost",
    "My-Header-Bad": "Good",
    "My-Header-Good": "Good",
    "My-Header-Set": "Set",
  },
  "origin": "127.0.0.1, 90.171.125.86",
  "url": "https://localhost:8080/get?var=good"
}

我们可以看到卷曲响应是配置请求过滤器的结果。他们补充我的报头-好与价值良好,并映射其内容以我的报头-坏。他们删除My-Header-Remove 并将新值设置为My-Header-Set。在argsurl部分,我们可以看到添加了一个新参数var。此外,最后一个过滤器将删除var2参数。

另外,我们可以在到达代理服务之前修改请求正文。只能使用Java DSL表示法配置此过滤器。下面的代码段仅将响应正文的内容大写:

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
     return builder.routes()
       .route("modify_request_body", r -> r.path("/post/**")
         .filters(f -> f.modifyRequestBody(
           String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE, 
           (exchange, s) -> Mono.just(new Hello(s.toUpperCase()))))
         .uri("https://httpbin.org"))
       .build();
}

要测试代码段,让我们使用*-d选项执行curl以包括主体“ Content”*:

$ curl -X POST "http://localhost:8080/post" -i -d "Content"
"data": "{\"message\":\"CONTENT\"}",
"json": {
    "message": "CONTENT"
}

我们可以看到,由于过滤器的原因,主体内容现在大写为CONTENT

3.2 HTTP响应

同样,我们可以通过使用addAddResponseHeader),设置或替换(SetResponseHeader),删除RemoveResponseHeader)和重写RewriteResponseHeader来修改响应头。响应上的另一个功能是重复数据删除DedupeResponseHeader)可以覆盖策略并避免重复。通过使用另一个内置工厂(RemoveLocationResponseHeader),我们可以摆脱有关版本,位置和主机的特定于后端的详细信息。

让我们看一个完整的例子:

- id: response_header_route
  uri: https://httpbin.org
  predicates:
  - Path=/header/post/**
  filters:
  - AddResponseHeader=My-Header-Good,Good
  - AddResponseHeader=My-Header-Set,Good
  - AddResponseHeader=My-Header-Rewrite, password=12345678
  - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
  - AddResponseHeader=My-Header-Remove,Remove
  - SetResponseHeader=My-Header-Set, Set
  - RemoveResponseHeader=My-Header-Remove
  - RewriteResponseHeader=My-Header-Rewrite, password=[^&]+, password=***
  - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,

让我们使用curl显示响应头:

$ curl -X POST "http://localhost:8080/header/post" -s -o /dev/null -D -
HTTP/1.1 200 OK
My-Header-Good: Good
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
My-Header-Rewrite: password=***
My-Header-Set: Set

与HTTP请求类似,我们可以修改响应正文。在此示例中,我们覆盖了PUT响应的主体:

@Bean
public RouteLocator responseRoutes(RouteLocatorBuilder builder) {
    return builder.routes()
      .route("modify_response_body", r -> r.path("/put/**")
        .filters(f -> f.modifyResponseBody(
          String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE, 
          (exchange, s) -> Mono.just(new Hello("New Body"))))
        .uri("https://httpbin.org"))
      .build();
}

让我们使用PUT端点来测试功能:

$ curl -X PUT "http://localhost:8080/put" -i -d "CONTENT"
{"message":"New Body"}

3.3 路径

内置WebFilter工厂提供的功能之一是与客户端配置的路径进行交互。可以设置其他路径SetPath),重写RewritePath),添加前缀PrefixPath)和剥离StripPrefix)以仅提取其中的一部分。请记住,过滤器是根据其在YAML文件中的位置顺序执行的。让我们看看如何配置路由:

- id: path_route
  uri: https://httpbin.org
  predicates:
  - Path=/new/post/**
  filters:
  - RewritePath=/new(?<segment>/?.*), $\{segment}
  - SetPath=/post

两个过滤器在到达代理服务之前都删除子路径*/ new*。让我们执行curl:

$ curl -X POST "http://localhost:8080/new/post" -i
"X-Forwarded-Prefix": "/new"
"url": "https://localhost:8080/post"

我们也可以使用StripPrefix工厂。使用StripPrefix = 1, 我们可以在联系下游服务时摆脱第一个子路径。

3.4 与HTTP状态有关

RedirectTo具有两个参数:status和URL。该状态必须是一系列300重定向HTTP代码,而URL必须是有效的。SetStatus 采用一种参数状态,可以是HTTP代码或其字符串表示形式。让我们看几个例子:

- id: redirect_route
  uri: https://httpbin.org
  predicates:
  - Path=/fake/post/**
  filters:
  - RedirectTo=302, https://httpbin.org
- id: status_route
  uri: https://httpbin.org
  predicates:
  - Path=/delete/**
  filters:
  - SetStatus=401

第一个过滤器作用于*/ fake / post*路径,并且客户端以HTTP状态302重定向到*https://httpbin.org*

$ curl -X POST "http://localhost:8080/fake/post" -i
HTTP/1.1 302 Found
Location: https://httpbin.org

第二个过滤器检测到*/ delete*路径,并设置了HTTP状态401

$ curl -X DELETE "http://localhost:8080/delete" -i
HTTP/1.1 401 Unauthorized

3.5 请求大小限制

最后,我们可以限制请求的大小限制RequestSize)。如果请求大小超出限制,则网关拒绝访问该服务

- id: size_route
  uri: https://httpbin.org
  predicates:
  - Path=/anything
  filters:
  - name: RequestSize
    args:
       maxSize: 5000000

4.高级用例

Spring Cloud Gateway提供了其他高级WebFilter工厂,以支持微服务模式的基准功能。

4.1 断路器

Spring Cloud Gateway具有内置的WebFilter工厂以提供断路器功能。工厂允许使用不同的后备策略和Java DSL路由配置。让我们看一个简单的例子:

- id: circuitbreaker_route
  uri: https://httpbin.org
  predicates:
  - Path=/status/504
  filters:
  - name: CircuitBreaker
  args:
     name: myCircuitBreaker
     fallbackUri: forward:/anything
  - RewritePath=/status/504, /anything

对于断路器的配置,我们通过添加[spring-cloud-starter-circuitbreaker-reactor-resilience4j](https://search.maven.org/search?q=g:org.springframework.cloud a:spring-cloud-starter-circuitbreaker-reactor-resilience4j)依赖性来使用Resilience4J

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>

同样,我们可以使用curl测试功能:

$ curl http://localhost:8080/status/504 
"url": "https://localhost:8080/anything"

4.2 重试

当代理服务发生问题时,另一个高级功能允许客户端重试访问。它需要几个参数,例如重试次数,HTTP状态代码(状态)和应重试的方法系列异常退避间隔,以在每次重试后等待。让我们看一下YAML配置:

- id: retry_test
  uri: https://httpbin.org
  predicates:
  - Path=/status/502
  filters:
  - name: Retry
    args:
       retries: 3
       statuses: BAD_GATEWAY
       methods: GET,POST
       backoff:
          firstBackoff: 10ms
          maxBackoff: 50ms
          factor: 2
          basedOnPreviousValue: false

当客户端到达*/ status / 502*(错误的网关)时,过滤器重试3次,等待每次执行后配置的退避间隔。让我们看看它是如何工作的:

$ curl http://localhost:8080/status/502

同时,我们需要检查服务器中的网关日志:

Mapping [Exchange: GET http://localhost:8080/status/502] to Route{id='retry_test', ...}
Handler is being applied: {uri=https://httpbin.org/status/502, method=GET}
Received last HTTP packet
Handler is being applied: {uri=https://httpbin.org/status/502, method=GET}
Received last HTTP packet
Handler is being applied: {uri=https://httpbin.org/status/502, method=GET}
Received last HTTP packet

当网关收到状态502时,过滤器将使用此退避重试方法GET和POST三次。

4.3 保存会话并保护标题

SecureHeader工厂增加HTTP安全标头的响应。同样,与Spring SessionSpring Security一起使用时,SaveSession尤为重要:

filters: 
- SaveSession

该过滤器 在进行转接呼叫之前存储会话状态

4.4 请求速率限制器

最后但并非最不重要的一点是,RequestRateLimiter工厂确定请求是否可以继续。如果不是,它将返回HTTP代码状态429 –太多请求它使用不同的参数和解析器来指定速率限制器

RedisRateLimiter使用众所周知的Redis数据库来检查令牌桶可以保持数。它需要以下依赖项:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
 </dependency>

因此,它还需要配置Spring Redis

spring:
  redis:
    host: localhost
    port: 6379

过滤器具有多个属性。第一个参数replenishRate是每秒允许的请求数。第二个参数burstCapacity是一秒钟内的最大请求数。第三个参数,*requestedTokens,*是请求花费多少个令牌。让我们看一个示例实现:

- id: request_rate_limiter
  uri: https://httpbin.org
  predicates:
  - Path=/redis/get/**
  filters:
  - StripPrefix=1
  - name: RequestRateLimiter
    args:
       redis-rate-limiter.replenishRate: 10
       redis-rate-limiter.burstCapacity: 5

让我们使用curl来测试过滤器。事先,请记住启动Redis实例,例如使用Docker

$ curl "http://localhost:8080/redis/get" -i
HTTP/1.1 200 OK
X-RateLimit-Remaining: 4
X-RateLimit-Requested-Tokens: 1
X-RateLimit-Burst-Capacity: 5
X-RateLimit-Replenish-Rate: 10

一旦剩余速率限制达到零,网关就会引发HTTP代码429。为了测试行为,我们可以使用单元测试。我们启动嵌入式Redis服务器并并行运行RepeatedTests。值区达到限制后,就会开始显示错误:

00:57:48.263 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->200, reason->OK, remaining->[4]
00:57:48.394 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->200, reason->OK, remaining->[3]
00:57:48.530 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->200, reason->OK, remaining->[2]
00:57:48.667 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->200, reason->OK, remaining->[1]
00:57:48.826 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->200, reason->OK, remaining->[0]
00:57:48.851 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->429, reason->Too Many Requests, remaining->[0]
00:57:48.894 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->429, reason->Too Many Requests, remaining->[0]
00:57:49.135 [main] INFO  c.b.s.w.RedisWebFilterFactoriesLiveTest - Received: status->200, reason->OK, remaining->[4]

翻译于:

https://www.baeldung.com/spring-cloud-gateway-webfilter-factories


QQ群【837324215】
关注我的公众号【Java大厂面试官】,回复:常用工具资源等关键词(更多关键词,关注后注意提示信息)获取更多免费资料。

公众号也会持续输出高质量文章,和大家共同进步。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lakernote

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值