Spring Cloud Gateway微服务网关
概述
网关简介
- 网关是系统唯一对外的入口,介于客户端与服务器端之间,用于对请求进行鉴权、限流、路由、监控等功能。
Gateway简介

- 这个项目提供了一个建立在 Spring 生态系统之上的 API 网关,包括:Spring 5、Spring Boot 2 和 project Reactor。Spring Cloud Gateway 旨在提供一种简单而有效的方法来路由到 api,并为它们提供跨领域的关注点,例如:安全性、监控/度量和弹性。
Gateway与Zuul的对比
Zuul
- Zuul 是由 Netflix 开源的 API 网关,基于 servlet 的,使用阻塞 API,它不支持任何长连接。
Gateway
- Spring Cloud Gateway 是 Spring Cloud 自己开发的开源 API 网关,建立在 Spring5,Reactor 和 Spring Boot 2 之上,使用非阻塞 API,支持异步开发。
Zuul 闭源
- Spring Cloud Gateway 是 Zuul 网关的替代者。只所以弃用 Zuul 并不是因为 Zuul 在功能有什么大的问题,而是因为最开始的 Zuul 是开源的,所以 Spring Cloud 就集成了 Zuul 做网关。但后来 Zuul 又宣布闭源,所以 Spring Cloud 自己开发了 Spring Cloud Gateway。再后来 Zuul 2.0 又开源了,但 Spring Cloud 不再集成它了。
工作原理
重要概念
- 在 Spring Cloud Gateway 中有三个非常重要的概念:
route 路由
- 路由是网关的最基本组成,由一个路由 id、一个目标地址 url、一组断言工厂及一组 filter 组成。若断言为 true,则请求将经由 filter 被路由到目标 url。
predicate 断言
- 断言即一个条件判断,根据当前的 http 请求进行指定规则的匹配,比如说 http 请求头,请求时间等。只有当匹配上规则时,断言才为 true,此时请求才会被直接路由到目标地址(目标服务器),或先路由到某过滤器链,经过过程器链的层层处理后,再路由到相应的目标地址(目标服务器)。
filter 过滤器
- 对请求进行处理的逻辑部分。当请求的断言为 true 时,会被路由到设置好的过滤器,以对请求进行处理。例如,可以为请求添加一个请求头,或添加一个请求参数,或对请求 URI 进行修改等。总之,就是对请求进行处理。
工作原理解析
- 下图从总体上概述了Spring Cloud Gateway的工作方式:

- 客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。该处理程序通过特定于请求的过滤器链运行请求。筛选器由虚线分隔的原因是,筛选器可以在发送代理请求之前和之后运行逻辑。所有“前置”过滤器逻辑均被执行。然后发出代理请求。发出代理请求后,将运行“后”过滤器逻辑。
示例演示
示例一
配置式路由 05-gateway-config-9000
- 创建工程:创建一个 Spring Initializr 工程,并命名为 05-gateway-config-9000。
- 定义pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.yw.sca.example</groupId>
<artifactId>05-gateway-config-9000</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@SpringBootApplication
public class Gateway059000 {
public static void main(String[] args) {
SpringApplication.run(Gateway059000.class, args);
}
}
server:
port: 9000
spring:
cloud:
gateway:
routes:
- id: weight_baidu_route
uri: http://www.baidu.com
predicates:
- Weight=search, 8
- id: weight_360_route
uri: http://www.so.com
predicates:
- Weight=search, 2

- 测试:不断刷新访问 http://localhost:9000/,会跳转到百度或360,比例 4: 1。
API 路由 05-gateway-api-9001
- 创建工程:复制工程 05-gateway-config-9000,并重命名为 05-gateway-api-9001。
- 修改 application.yml 配置文件:去掉原来配置的路由策略,仅剩如下内容。
server:
port: 9001
- 修改启动类:在启动类中添加一个@Bean 方法,用于设置路由策略。
@SpringBootApplication
public class Gateway059001 {
public static void main(String[] args) {
SpringApplication.run(Gateway059001.class, args);
}
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(ps -> ps.path("/**").uri("https://www.baidu.com").id("baidu_route"))
.build();
}
}
- 启动工程,访问地址 http://localhost:9001/ 即可跳转至百度。
示例二
- 根据微服务名称进行 Ribbon 负载均衡:有一个微服务,有三个提供者,这里要通过 spring cloud gateway 实现对该微服务的负载均衡访问。
配置式路由 05-gateway-ribbon-config-9000
- 创建工程:复制工程 05-gateway-config-9000,并重命名为 05-gateway-ribbon-config-9000。
- 添加依赖:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
server:
port: 9000
spring:
application:
name: msc-gateway-depart
cloud:
nacos:
discovery:
server-addr: 192.168.254.128:8848
gateway:
discovery:
locator:
enabled: true
routes:
- id: ribbon_route
predicates:
- Path=/provider/depart/**
uri: lb://msc-provider-depart
- 利用前面的 04-provider-nacos-8081 工程,启动三个 provider 工程,端口分别是 8081、8082、8083。
- 再启动 05-gateway-ribbon-config-9000 工程,测试访问 http://localhost:9000/provider/depart/get/1,测试下负载均衡的效果。
API 路由 05-gateway-ribbon-api-9001
- 创建工程:复制工程 05-gateway-ribbon-config-9000,并重命名为 05-gateway-ribbon-api-9001。
- 修改 application.yml 配置文件:
server:
port: 9001
spring:
application:
name: msc-gateway-depart
cloud:
nacos:
discovery:
server-addr: 192.168.254.128:8848
@SpringBootApplication
public class Gateway059001 {
public static void main(String[] args) {
SpringApplication.run(Gateway059001.class, args);
}
@Bean
public RouteLocator someRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(ps -> ps.path("/provider/depart/**")
.uri("lb://msc-provider-depart")
.id("ribbon_route"))
.build();
}
}
- 同样启动 05-gateway-ribbon-api-9001 工程,测试访问 http://localhost:9001/provider/depart/get/1,测试下负载均衡的效果。
路由断言工厂
简介
- Spring Cloud Gateway 将路由匹配作为最基本的功能。而这个功能是通过路由断言工厂完成的。Spring Cloud Gateway 中包含了许多内置的路由断言工厂。所有这些断言都可以匹配 HTTP 请求的不同属性,并且可以根据逻辑与状态,将多个路由断言工厂复合使用。
- 路由断言工厂的使用方式有两种:配置方式与 API 方式。
After 路由断言工厂
- 规则:该断言工厂的参数是一个 UTC 格式的时间。其会将请求访问到 Gateway 的时间与该参数时间相比,若请求时间在参数时间之后,则匹配成功,断言为 true。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://www.baidu.com
predicates:
- After=2017-01-20T17:42:47.789-07:00[Asia/Shanghai]
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
ZonedDateTime dateTime = LocalDateTime.now().minusDays(5)
.atZone(ZoneId.systemDefault());
return builder.routes()
.route(ps -> ps.after(dateTime)
.uri("https://www.baidu.com")
.id("after_route"))
.build();
}
Before 路由断言工厂
- 规则:该断言工厂的参数是一个 UTC 格式的时间。其会将请求访问到 Gateway 的时间与该参数时间相比,若请求时间在参数时间之前,则匹配成功,断言为 true。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://www.baidu.com
predicates:
- Before=2017-01-20T17:42:47.789-07:00[Asia/Shanghai]
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
ZonedDateTime dateTime = LocalDateTime.now().minusDays(5)
.atZone(ZoneId.systemDefault());
return builder.routes()
.route(ps -> ps.before(dateTime)
.uri("https://www.baidu.com")
.id("before_route"))
.build();
}
Between 路由断言工厂
- 规则:该断言工厂的参数是两个 UTC 格式的时间。其会将请求访问到 Gateway 的时间与这两个参数时间相比,若请求时间在这两个参数时间之间,则匹配成功,断言为 true。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://www.baidu.com
predicates:
- Between=2017-01-20T17:42:47.789-07:00[Asia/Shanghai],2021-02-20T17:42:47.789-07:00[Asia/Shanghai]
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
ZonedDateTime minusTime = LocalDateTime.now().minusDays(5)
.atZone(ZoneId.systemDefault());
ZonedDateTime plusTime = LocalDateTime.now().plusDays(3)
.atZone(ZoneId.systemDefault());
return builder.routes()
.route("between_route", ps -> ps.between(minusTime, plusTime)
.uri("https://www.baidu.com"))
.build();
}
Cookie 路由断言工厂
- 规则:该断言工厂中包含两个参数,分别是 cookie 的 key 与 value。当请求中携带了指定 key 与 value 的 cookie 时,匹配成功,断言为 true。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://www.baidu.com
predicates:
- Cookie=chocolate, dove
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("cookie_route", ps -> ps.cookie("chocolate", "regex")
.uri("https://www.baidu.com"))
.build();
}
Header 路由断言工厂
- 规则:该断言工厂中包含两个参数,分别是请求头 header 的 key 与 value。当请求中携带了指定 key 与 value 的 header 时,匹配成功,断言为 true。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://www.baidu.com
predicates:
- Header=X-Request-Id, \d+
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("header_route", ps -> ps.header("X-Request-Id", "\\d+")
.uri("https://www.baidu.com"))
.build();
}
Host 路由断言工厂
- 规则:该断言工厂中包含的参数是请求头中的 Host 属性。当请求中携带了指定的 Host 属性值时,匹配成功,断言为 true。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://www.baidu.com
predicates:
- Host=mylocalhost:9000, myhost:9000
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("host_route", ps -> ps.host("myhost:9000")
.uri("https://www.baidu.com"))
.build();
}
Method 路由断言工厂
- 规则:该断言工厂用于判断请求是否使用了指定的请求方法,是 POST,还是 GET 等。当请求中使用了指定的请求方法时,匹配成功,断言为 true。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://www.baidu.com
predicates:
- Method=GET, POST
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("method_route", ps -> ps.method("POST")
.uri("https://www.baidu.com"))
.build();
}
Path 路由断言工厂
- 规则:该断言工厂用于判断请求路径中是否包含指定的 uri。若包含,则匹配成功,断言为 true,此时会将该匹配上的 uri 拼接到要转向的目标 uri 的后面,形成一个统一的 uri。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: provider_route
uri: http://localhost:8081
predicates:
- Path=/provider/**
- id: consumer_route
uri: http://localhost:8080
predicates:
- Path=/consumer/**
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("provider_route", ps -> ps.path("/provider/**").uri("http://localhost:8081"))
.route("consumer_route", ps -> ps.path("/consumer/**").uri("http://localhost:8080"))
.build();
}
Query 路由断言工厂
- 规则:该断言工厂用于从请求中查找指定的请求参数。其可以只查看参数名称,也可以同时查看参数名与参数值。当请求中包含了指定的参数名,或名值对时,匹配成功,断言为 true。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://www.baidu.com
predicates:
- Query=color, gr.+
- Query=size, 5
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("query_route", ps -> ps.query("color", "gr.+")
.and()
.query("size", "5")
.uri("http://localhost:8081"))
.build();
}
RemoteAddr 路由断言工厂
- 规则:该断言工厂用于判断请求提交的所要访问的 IP 地址是否在断言中指定的 IP 范围。当请求中的 IP 在指定范围时,匹配成功,断言为 true。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: remoteAddr_route
uri: https://www.baidu.com
predicates:
- RemoteAddr=192.168.0.100/24
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("remoteAddr_route", ps -> ps.remoteAddr("192.168.3.1/24")
.uri("https://www.baidu.com "))
.build();
}
Weight 路由断言工厂
- 规则:该断言工厂中包含两个参数,分别是用于表示组 group,与权重 weight。对于同一组中的多个 uri 地址,路由器会根据设置的权重,按比例将请求转发给相应的 uri。实现负载均衡。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: weight_baidu_route
uri: https://www.baidu.com
predicates:
- Weight=search, 8
- id: weight_360_route
uri: https://www.so.com
predicates:
- Weight=search, 2
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("weight_baidu_route", ps -> ps.weight("search", 8)
.uri("https://www.baidu.com"))
.route("weight_360_route", ps -> ps.weight("search", 2)
.uri("https://www.so.com"))
.build();
}
GatewayFilter工厂
- 过滤器允许以某种方式修改传入的 HTTP 请求或返回的 HTTP 响应。过滤器的作用域是某些特定路由。Spring Cloud Gateway 包括许多内置的 GatewayFilter 工厂。
- GatewayFilter 工厂的使用方式有两种:配置方式与 API 方式。
- 创建两个工程 05-gateway-filter-config-9000、05-gateway-filter-api-9001 进行测试。
定义三个工程
web工程 05-showinfo-8080
- 创建一个普通的 Spring Boot 工程,仅导入一个 Spring Web 依赖,即spring-boot-web-starter 依赖。
- 定义处理器:
@RestController
@RequestMapping("/info")
public class ShowInfoController {
@RequestMapping("header")
public String headerHandler(HttpServletRequest request) {
return "X-Request-Red: " + request.getHeader("X-Request-Red");
}
@RequestMapping("/uri")
public String uriHandler(HttpServletRequest request) {
return "uri: " + request.getRequestURI();
}
@RequestMapping("/param")
public String paramHandler(String color) {
return "color: " + color;
}
@RequestMapping("/time")
public String timeHandler() {
return "time: " + System.currentTimeMillis();
}
}
创建过滤器工程
- 配置方式:创建 config 工程 05-gateway-filter-config-9000,直接复制 05-gateway-config-9000 工程,并将配置文件中原来设置的路由策略全部删除,后续对于 filter 的配置式设置,都在该工程中进行。
- API方式:创建 API 工程 05-gateway-filter-api-9001,直接复制 05-gateway-api-9001 工程,并将启动类中原来设置的路由策略全部删除,后续对于 filter 的 API 式设置,都在该工程中进行。
AddRequestHeader 过滤工厂
- 规则:该过滤器工厂会对匹配上的请求添加指定的 header。
- 配置方式:测试地址 http://localhost:9000/info/header
spring:
cloud:
gateway:
routes:
- id: addRequestHeader_filter
uri: http://localhost:8080
predicates:
- Path=/**
filters:
- AddRequestHeader=X-Request-Color, blue
- API 方式:测试地址 http://localhost:9001/info/header
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("addRequestHeader_filter", ps -> ps.path("/**")
.filters(fs -> fs.addRequestHeader("X-Request-Red", "Red"))
.uri("http://localhost:8080")
).build();
}
AddRequestParameter 过滤工厂
- 规则:该过滤器工厂会对匹配上的请求添加指定的请求参数。
- 配置方式:测试地址 http://localhost:9000/info/param
spring:
cloud:
gateway:
routes:
- id: addRequestParameter_filter
uri: http://localhost:8080
predicates:
- Path=/**
filters:
- AddRequestParameter=color, blue
- API 方式:测试地址 http://localhost:9001/info/param
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("addRequestParameter_filter", ps -> ps.path("/**")
.filters(fs -> fs.addRequestParameter("color", "Red"))
.uri("http://localhost:8080")
).build();
}
AddResponseHeader 过滤工厂
- 规则:该过滤器工厂会给从网关返回的响应添加上指定的 header。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: addResponseHeader_filter
uri: http://localhost:8080
predicates:
- Path=/**
filters:
- AddResponseHeader=X-Response-Color, blue
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("addResponseHeader_filter", ps -> ps.path("/**")
.filters(fs -> fs.addResponseHeader("X-Response-Color", "Red"))
.uri("http://localhost:8080")
).build();
}
PrefixPath 过滤工厂
- 规则:该过滤器工厂会为指定的 URI 自动添加上一个指定的 URI 前辍。
- 配置方式:
spring:
cloud:
gateway:
routes:
- id: prefixPath_filter
uri: http://localhost:8080
predicates:
- Path=/**
filters:
- PrefixPath=/info
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("prefixPath_filter", ps -> ps.path("/**")
.filters(fs -> fs.prefixPath("/info"))
.uri("http://localhost:8080")
).build();
}
StripPrefix 过滤工厂
- 规则:该过滤器工厂会为指定的 URI 去掉指定长度的前辍。
- 配置方式:测试地址 http://localhost:9000/aa/bb/info/uri
spring:
cloud:
gateway:
routes:
- id: stripPrefix_filter
uri: http://localhost:8080
predicates:
- Path=/**
filters:
- StripPrefix=2
- API 方式:测试地址 http://localhost:9001/aa/bb/info/uri
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("stripPrefix_filter", ps -> ps.path("/**")
.filters(fs -> fs.stripPrefix(2))
.uri("http://localhost:8080")
).build();
}
RewritePath 过滤工厂
- 规则:该过滤器工厂会将请求 URI 替换为另一个指定的 URI 进行访问,RewritePath 有两个参数,第一个是正则表达式,第二个是要替换为的目标表达式。
- 配置方式:测试地址 http://localhost:9000/red/info/uri
spring:
cloud:
gateway:
routes:
- id: rewritePath_filter
uri: http://localhost:8080
predicates:
- Path=/**
filters:
- RewritePath=/red(?<segment>/?.*), $\{segment}
- API 方式:测试地址 http://localhost:9001/red/info/uri
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewritePath_filter", ps -> ps.path("/**")
.filters(fs -> fs.rewritePath("/red(?<segment>/?.*)", "${segment}"))
.uri("http://localhost:8080")
).build();
}
CircuitBreaker 过滤工厂
- 规则:该过滤器工厂完成网关层的服务熔断与降级。
- 添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
- 需求:我们下面的例子实现的需求是,浏览器访问 05-showinfo-8080 的处理器不成功,从而服务降级到 Gateway 工程中定义的降级处理器。
- 定义降级处理器:05-gateway-filter-config-9000 与 05-gateway-filter-api-9001 两个工程中均需要添加上该降级处理器。
@RestController
public class GatewayFallbackController {
@GetMapping("/fallback")
public String fallbackHandler() {
return "This is the Gateway Fallback";
}
}
- 配置方式:停掉 05-showinfo-8080 工程,测试地址 http://localhost:9000/info/uri
spring:
cloud:
gateway:
routes:
- id: circuitBreaker_filter
uri: http://localhost:8080
predicates:
- Path=/**
filters:
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/fallback
- API 方式:停掉 05-showinfo-8080 工程,测试地址 http://localhost:9001/info/uri
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("circuitBreaker_filter", ps -> ps.path("/**")
.filters(fs -> fs.circuitBreaker(config -> {
config.setName("myCircuitBreaker");
config.setFallbackUri("forward:/fallback");
}))
.uri("http://localhost:8080")
).build();
}
RequestRateLimiterGateway 过滤工厂
- 规则:该过滤器工厂会对进来的请求进行限流。这里采用的是令牌桶算法。另外,从这里也可以看出其是基于 Redis 实现的,所以需要导入 Redis 的依赖。
- 添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
spring:
cloud:
gateway:
routes:
- id: requestRateLimiter_filter
uri: http://localhost:8080
predicates:
- Path=/**
filters:
- name: RequestRateLimiter
args:
key-resolver: "#{@keyResolver}"
redis-rate-limiter.replenishRate: 2
redis-rate-limiter.burstCapacity: 5
redis-rate-limiter.requestedTokens: 1
@Bean
public KeyResolver keyResolver() {
return exchange -> Mono.just(exchange.getRequest()
.getRemoteAddress().getHostName());
}
默认 Filters 工厂
- 前面的 GatewayFilter 工厂是在某一特定路由策略中设置的,仅对这一种路由生效。若要使某些过滤效果应用到所有路由策略中,就可以将该 GatewayFilter 工厂定义在默认 Filters 中。修改 gateway 工程配置文件。
spring:
cloud:
gateway:
default-filters:
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/fallback
routes:
- id: test_filter
uri: http://localhost:8080
predicates:
- Path=/**
优先级
- 局部 filter 的优先级要高于默认 Filter 的。
- 相同路由策略,配置式的要高于 API 式的。
自定义Filter
- 前面的 GatewayFilter 工厂可以创建出某种特定的 Filter 过滤效果,但这些过滤功能可能并不能满足真正的业务需求,此时可以根据具体需求自定义自己的 Filter。
Filter 基础
Filter 分类
- 过滤器允许以某种方式修改传入的 HTTP 请求或返回的 HTTP 响应。Filter 根据其作用范围的不同,分为两种:Gateway Filter 与 Global Filter。
- Gateway Filter 应用于单个路由策略上,其功能仅对路由断言为 true 的请求起作用。
- Global Filter 应用于所有路由策略上,其功能就像前面的配置文件中设置的默认 Filter。
Filter 获取方式
- 这些 Filter 有三种获取方式:通过内置的 Filter 工厂获取、系统内置的 Filter,及自定义Filter。对于这两种 Filter 的获取与使用,有以下说明。
- GatewayFilter:对于该类型 Filter 的使用,直接通过系统中内置的 GatewayFilter 工厂的配置即可使用。
- GlobalFilter:系统中已经定义了一些 Global Filter,而这些 Filter 的使用一般不是显式的使用,在潜移默化中就使用了。例如,在自定义了一些 GlobalFilter 后,为了保证这些Filter 的执行顺序,在每个返回 GlobalFilter 的@Bean 方法上添加@Order,或直接使自定义的 GlobalFilter 类实现 Order 接口,就是对 Combined Global Filter 的使用。再例如,在完成负载均衡时,router 的 uri 属性值以 lb://开头,就是对 LoadBalancerClient Filter 的使用。

自定义 GatewayFilter-修改请求
- 这里要实现的需求是,在自定义的 Filter 中为请求添加指定的请求头。
- 创建工程:复制 05-gateway-api-9001 工程,并重命名为 05-custom-gatewayfilter-9000。在此工程基础上进行修改,首先要修改端口号为 9000。
- 定义GatewayFilter:
public class AddHeaderGatewayFilter implements GatewayFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest httpRequest = exchange.getRequest()
.mutate()
.header("X-Request-Red", "blue")
.build();
ServerWebExchange webExchange = exchange.mutate()
.request(httpRequest)
.build();
return chain.filter(webExchange);
}
}
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("custom_filter_route", ps -> ps.path("/**")
.filters(f -> f.filter(new AddHeaderGatewayFilter()))
.uri("http://localhost:8080")
)
.build();
}
- 测试地址:http://localhost:9000/info/header
自定义 GatewayFilter-多 filter
- 下面我们要定义出多个 Filter,每个 Filter 都具有 pre 与 post 两部分。将所有 Filter 注册到路由中,以查看它们执行的顺序。
- 创建工程:复制 05-custom-gatewayfilter-9000 工程,并重命名为 05-custom-multifilters-9000。在此工程基础上进行修改。
- 定义三个 GatewayFilter:
@Slf4j
public class OneGatewayFilter implements GatewayFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long startTime = System.currentTimeMillis();
log.info("pre-filter-111 " + startTime);
exchange.getAttributes().put("startTime", startTime);
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
log.info("post-filter-111");
long startTimeAttr = (long) exchange.getAttributes().get("startTime");
long elapsedTime = System.currentTimeMillis() - startTimeAttr;
log.info("该过滤器执行用时 = " + elapsedTime);
})
);
}
}
@Slf4j
public class TwoGatewayFilter implements GatewayFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("pre-filter-222");
return chain.filter(exchange).then(
Mono.fromRunnable(() -> log.info("post-filter-222 " + System.currentTimeMillis()))
);
}
}
@Slf4j
public class ThreeGatewayFilter implements GatewayFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("pre-filter-333");
return chain.filter(exchange).then(
Mono.fromRunnable(() -> log.info("post-filter-333 " + System.currentTimeMillis()))
);
}
}

- 启动工程,进行测试访问 http://localhost:9000/info/time,验证效果

自定义 GlobalFilter
- Global Filter 不需要在任何具体的路由规则中进行注册,只需在类上添加@Compoment注解,将其生命周期交给 Spring 容器来管理即可。
- 需求:这里要实现的需求是,访问当前系统的任意模块的 URL 都需要是合法的 URL。这里所谓合法的 URL 指的是请求中携带了 token 请求参数。由于是对所有请求的 URL 都要进行验证,所以这里就需要定义一个 Global Filter,可以应用到所有路由中。
- 创建工程:复制 05-custom-gatewayfilter-9000 工程,并重命名为 05-custom-globalfilter-9000。在此工程基础上进行修改。
- 定义 GlobalFilter:
@Component
public class UrlValidateFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtils.isEmpty(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
