Spring Cloud Gateway 为 SpringBoot 应用提供了 API 网关支持,具有强大的智能路由与过滤器功能,本文将对其用法进行详细介绍。
Gateway 简介
Gateway 是在 Spring 生态系统之上构建的 API 网关服务,基于 Spring 5,Spring Boot 2 和 Project Reactor 等技术。Gateway 旨在提供一种简单而有效的方式来对 API 进行路由,以及提供一些强大的过滤器功能, 例如:熔断、限流、重试等。
Spring Cloud Gateway 具有如下特性:
- 基于 Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建;
- 动态路由:能够匹配任何请求属性;
- 可以对路由指定 Predicate(断言)和 Filter(过滤器);
- 集成 Hystrix 的断路器功能;
- 集成 Spring Cloud 服务发现功能;
- 易于编写的 Predicate(断言)和 Filter(过滤器);
- 请求限流功能;
- 支持路径重写。
相关概念
- Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由; - Predicate(断言):指的是 Java 8 的 Function Predicate。 输入类型是 Spring 框架中的 ServerWebExchange。 这使开发人员可以匹配 HTTP 请求中的所有内容,例如请求头或请求参数。如果请求与断言相匹配,则进行路由; - Filter(过滤器):指的是 Spring 框架中 GatewayFilter 的实例,使用过滤器,可以在请求被路由前后对请求进行修改。
创建项目模块
这里我们创建一个 api-gateway 模块来演示 Gateway 的常用功能。
引入依赖
在 pom.xml 中添加相关依赖:
1 | <dependency> |
相关配置
Gateway 提供了两种不同的方式用于配置路由,一种是通过 yml 文件来配置,另一种是通过 Java Bean 来配置,下面我们分别介绍下。
方式一:使用 yml 配置
在 application.yml 中进行配置:
1 | server: |
启动 eureka-server,user-service 和 api-gateway 服务,并调用该地址测试:http://localhost:9201/user/1,我们发现该请求被路由到了 user-service 的该路径上:http://localhost:8201/user/1
方式二:使用 Java Bean 配置
添加相关配置类,并配置一个 RouteLocator 对象:
1 |
|
重新启动 api-gateway 服务,并调用该地址测试:http://localhost:9201/user/getByUsername?username=antonio,我们发现该请求被路由到了 user-service 的该路径上:http://localhost:8201/user/getByUsername?username=antonio
Route Predicate 的使用
Spring Cloud Gateway 将路由匹配作为 Spring WebFlux HandlerMapping 基础架构的一部分。 Spring Cloud Gateway 包括许多内置的 Route Predicate 工厂。 所有这些 Predicate 都与 HTTP 请求的不同属性匹配。 多个 Route Predicate 工厂可以进行组合,下面我们来介绍下一些常用的 Route Predicate。
注意:Predicate 中提到的配置都在 application-predicate.yml 文件中进行修改,并用该配置启动 api-gateway 服务。
After Route Predicate
在指定时间之后的请求会匹配该路由。
1 | spring: |
Before Route Predicate
在指定时间之前的请求会匹配该路由。
1 | spring: |
Between Route Predicate
在指定时间区间内的请求会匹配该路由。
1 | spring: |
Cookie Route Predicate
带有指定 Cookie 的请求会匹配该路由。
1 | spring: |
使用 curl 工具发送带有 Cookie 为 username=antonio
的请求可以匹配该路由。
1 | $ curl http://localhost:9201/user/1 --cookie "username=antonio" |
Header Route Predicate
带有指定请求头的请求会匹配该路由。
1 | spring: |
使用 curl 工具发送带有请求头为 X-Request-Id:123
的请求可以匹配该路由。
1 | $ curl http://localhost:9201/user/1 -H "X-Request-Id:123" |
Host Route Predicate
带有指定 Host 的请求会匹配该路由。
1 | spring: |
使用 curl 工具发送带有请求头为 Host:www.antoniopeng.com
的请求可以匹配该路由。
1 | $ curl http://localhost:9201/user/1 -H "Host:www.antoniopeng.com" |
Method Route Predicate
发送指定方法的请求会匹配该路由。
1 | spring: |
使用 curl 工具发送 GET 请求可以匹配该路由。
1 | $ curl http://localhost:9201/user/1 |
使用 curl 工具发送 POST 请求无法匹配该路由。
1 | $ curl -X POST http://localhost:9201/user/1 |
Path Route Predicate
发送指定路径的请求会匹配该路由。
1 | spring: |
使用 curl 工具发送 /user/1
路径请求可以匹配该路由。
1 | $ curl http://localhost:9201/user/1 |
使用 curl 工具发送 /abc/1
路径请求无法匹配该路由。
1 | $ curl http://localhost:9201/abc/1 |
Query Route Predicate
带指定查询参数的请求可以匹配该路由。
1 | spring: |
使用 curl 工具发送带 username=antonio
查询参数的请求可以匹配该路由。
1 | $ curl http://localhost:9201/user/getByUsername?username=antonio |
使用 curl 工具发送带不带查询参数的请求无法匹配该路由。
1 | $ curl http://localhost:9201/user/getByUsername |
RemoteAddr Route Predicate
从指定远程地址发起的请求可以匹配该路由。
1 | spring: |
使用 curl 工具从 192.168.1.1 发起请求可以匹配该路由。
1 | $ curl http://localhost:9201/user/1 |
Weight Route Predicate
使用权重来路由相应请求,以下表示有 80% 的请求会被路由到 localhost:8201,20% 会被路由到 localhost:8202。
1 | spring: |
Route Filter 的使用
路由过滤器可用于修改进入的 HTTP 请求和返回的 HTTP 响应,路由过滤器只能指定路由进行使用。Spring Cloud Gateway 内置了多种路由过滤器,他们都由GatewayFilter 的工厂类来产生,下面我们介绍下常用路由过滤器的用法。
AddRequestParameter GatewayFilter
给请求添加参数的过滤器。
1 | spring: |
以上配置会对 GET 请求添加 username=antonio
的请求参数,通过 curl 工具使用以下命令进行测试。
1 | $ curl http://localhost:9201/user/getByUsername |
相当于发起该请求:
1 | $ curl http://localhost:8201/user/getByUsername?username=antonio |
StripPrefix GatewayFilter
对指定数量的路径前缀进行去除的过滤器。
1 | spring: |
以上配置会把以 /user-service/
开头的请求的路径去除两位,通过 curl 工具使用以下命令进行测试。
1 | $ curl http://localhost:9201/user-service/a/user/1 |
相当于发起该请求:
1 | $ curl http://localhost:8201/user/1 |
PrefixPath GatewayFilter
与 StripPrefix 过滤器恰好相反,会对原有路径进行增加操作的过滤器。
1 | spring: |
以上配置会对所有 GET 请求添加 /user
路径前缀,通过 curl 工具使用以下命令进行测试。
1 | $ curl http://localhost:9201/1 |
相当于发起该请求:
1 | $ curl http://localhost:8201/user/1 |
Hystrix GatewayFilter
Hystrix 过滤器允许你将断路器功能添加到网关路由中,使你的服务免受级联故障的影响,并提供服务降级处理。
要开启断路器功能,我们需要在 pom.xml 中添加 Hystrix 的相关依赖:
1 | <dependency> |
然后添加相关服务降级的处理类:
1 |
|
在 application-filter.yml 中添加相关配置,当路由出错时会转发到服务降级处理的控制器上:
1 | spring: |
关闭 user-service,调用该地址进行测试:http://localhost:9201/user/1,发现已经返回了服务降级的处理信息。
RequestRateLimiter GatewayFilter
RequestRateLimiter 过滤器可以用于限流,使用 RateLimiter 实现来确定是否允许当前请求继续进行,如果请求太大默认会返回 HTTP 429-太多请求状态。
在 pom.xml 中添加相关依赖:
1 | <dependency> |
添加限流策略的配置类,这里有两种策略一种是根据请求参数中的 username 进行限流,另一种是根据访问IP进行限流:
1 |
|
我们使用 Redis 来进行限流,所以需要添加 Redis 和 RequestRateLimiter 的配置,这里对所有的 GET 请求都进行了按 IP 来限流的操作:
1 | server: |
多次请求该地址:http://localhost:9201/user/1,会返回状态码为 429 的错误:
Retry GatewayFilter
对路由请求进行重试的过滤器,可以根据路由请求返回的HTTP状态码来确定是否进行重试。
修改 application.yaml 配置文件:
1 | spring: |
当调用返回 500 时会进行重试,访问测试地址:http://localhost:9201/user/111,可以发现 user-service 控制台报错 2 次,说明进行了一次重试。
1 | 2019-10-27 14:08:53.435 ERROR 2280 --- [nio-8201-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause |
结合注册中心使用
我们上次讲到使用 Zuul 作为网关结合注册中心进行使用时,默认情况下 Zuul 会根据注册中心注册的服务列表,以服务名为路径创建动态路由,Gateway 同样也实现了该功能。下面我们演示下 Gateway 结合注册中心如何使用默认的动态路由和过滤器。
使用动态路由
在 pom.xml 中添加相关依赖:
1 | <dependency> |
添加 application-eureka.yml 配置文件:
1 | server: |
使用 application-eureka.yml 配置文件启动 api-gateway 服务,访问 http://localhost:9201/user-service/user/1,可以路由到 user-service 的 http://localhost:8201/user/1 处。
使用过滤器
在结合注册中心使用过滤器的时候,我们需要注意的是 uri 的协议为 lb
,这样才能启用 Gateway 的负载均衡功能。
修改 application-eureka.yml 文件,使用了 PrefixPath 过滤器,会为所有 GET 请求路径添加 /user
路径并路由:
1 | server: |
使用 application-eureka.yml 配置文件启动 api-gateway 服务,访问 http://localhost:9201/1,可以路由到 user-service 的 http://localhost:8201/user/1 处。
更多干货请移步:https://antoniopeng.com
如果你喜欢这个博客或发现它对你有用,欢迎你点击右下角 “OPEN CHAT” 进行评论。也欢迎你分享这个博客,让更多的人参与进来。如果在博客中的内容侵犯了您的版权,请联系博主删除它们。谢谢你!