Appearance
Spring Cloud Gateway
1. 为什么需要网关
网关的功能:
- 身份认证和鉴权
- 服务路由、负载均衡
- 请求限流
- ……
Spring Cloud 中网关的实现主要包括两种:
- Gateway
- Zuul
Zuul 是基于 Servlet 的实现(阻塞式),而 Spring Cloud Gateway 则是基于 WebFlux(响应式),具备更好的性能。
2. 快速入门
创建新的 Module 或项目;
引入 Spring Cloud Gateway 和注册中心(以 Nacos 为例)相关的依赖;
父 POM Maven 依赖管理示例:
XML<properties> <spring-boot.version>2.7.6</spring-boot.version> <spring-cloud.version>2021.0.8</spring-cloud.version> <spring-alibaba-cloud.version>2021.0.6.0</spring-alibaba-cloud.version> </properties> <dependencyManagement> <dependencies> <!-- Spring Boot Dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- Spring Cloud Dependencies --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- Spring Cloud Alibaba Dependencies --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-alibaba-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>因为我们后续希望启用网关的负载均衡,所以我们这里还引入了 Spring Cloud LoadBalancer。Gateway 项目依赖:
XML<!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- Nacos Discovery --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- Spring Cloud LoadBalancer --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> <!-- Spring Cloud Gateway --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>编写注册中心地址及 Gateway 路由配置
YAMLserver: port: 10001 spring: application: name: gateway cloud: nacos: server-addr: your-nacos-addr:8848 username: your_username password: your_password discovery: ip: your_ip cluster-name: your_cluster_name gateway: routes: # 定义网关的路由规则列表 - id: user-service # 路由的唯一标识符,用于区分不同的路由规则 uri: lb://user-service # 目标服务的 URI,'lb://' 表示使用负载均衡,指向注册中心中的 'user-service' 服务 predicates: # 路由断言,用于匹配请求以决定是否转发到该路由 - Path=/user/** # 路径断言,匹配以 '/user/' 开头的所有请求('**' 表示任意子路径) - id: order-service uri: http://order-service # 'http://' 表示固定地址 predicates: - Path=/order/**
3. 断言工厂
我们在配置文件中写的断言规则只是字符串,这些字符串会被 RoutePredicateFactory 读取并处理,转变为路由判断的条件。例如 Path=/user/** 是按照路径匹配,这个规则是由 org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory 类来处理的像这样的。
断言工厂在 Spring Cloud Gateway 还有十几个:
| 名称 | 说明 | 示例 |
|---|---|---|
| After | 匹配在指定时间之后发起的请求 | - After=2017-01-20T17:42:47.789-07:00[America/Denver] |
| Before | 匹配在指定时间之前发起的请求 | - Before=2017-01-20T17:42:47.789-07:00[America/Denver] |
| Between | 匹配在两个指定时间之间发起的请求 | - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver] |
| Cookie | 匹配包含指定名称和值的 Cookie 的请求 | - Cookie=chocolate, ch.p |
| Header | 匹配包含指定请求头和值的请求(值支持正则表达式) | - Header=X-Request-Id, \d+ |
| Host | 匹配指定主机名的请求(支持通配符) | - Host=**.somehost.org,**.anotherhost.org |
| Method | 匹配指定 HTTP 方法的请求 | - Method=GET,POST |
| Path | 匹配指定路径模式的请求(支持 Ant 风格通配符和路径参数) | - Path=/red/{segment},/blue/{segment} |
| Query | 匹配包含指定查询参数(可选正则表达式值匹配)的请求 | - Query=red, gree. |
| RemoteAddr | 匹配来自指定 IP 地址或子网的请求 | - RemoteAddr=192.168.1.1/24 |
| Weight | 基于权重的路由分配,指定分组和权重值 | - Weight=group1, 2 |
| XForwarded Remote Addr | 匹配通过 X-Forwarded-For 头指定的客户端 IP 地址或子网的请求 | - XForwardedRemoteAddr=192.168.1.1/24 |
4. 过滤器工厂
Spring Cloud Gateway 提供多种过滤器工厂(Filter Factory),用于在网关路由后修改请求或响应。以下是常用的过滤器工厂:
| 名称 | 说明 | 示例 |
|---|---|---|
| AddRequestHeader | 为请求添加指定的请求头 | - AddRequestHeader=X-Request-Color, Blue |
| AddResponseHeader | 为响应添加指定的响应头 | - AddResponseHeader=X-Response-Color, Red |
| RemoveRequestHeader | 移除请求中指定的请求头 | - RemoveRequestHeader=X-Request-Id |
| RemoveResponseHeader | 移除响应中指定的响应头 | - RemoveResponseHeader=X-Response-Foo |
| SetPath | 修改请求的路径 | - SetPath=/newpath/{segment} |
| RewritePath | 使用正则表达式重写请求路径 | - RewritePath=/foo/(?<segment>.*), /bar/${segment} |
| AddRequestParameter | 为请求添加指定的查询参数 | - AddRequestParameter=color, blue |
| ModifyRequestBody | 修改请求体的内容 | |
| ModifyResponseBody | 修改响应体的内容 | |
| StripPrefix | 去除请求路径中指定数量的前缀部分 | - StripPrefix=2 |
| PrefixPath | 为请求路径添加指定的前缀 | - PrefixPath=/mypath |
| RedirectTo | 将请求重定向到指定的 URL 和状态码 | - RedirectTo=302, https://example.com |
| SetStatus | 设置响应的 HTTP 状态码 | - SetStatus=401 |
| SaveSession | 强制保存会话状态(通常用于与 Spring Session 集成) | - SaveSession |
| Retry | 配置请求重试策略(可指定重试次数、状态码等) | - Retry=3,methods=GET,statuses=503,backoff=100ms |
| CircuitBreaker | 集成断路器(如 Resilience4J),支持降级处理 | - CircuitBreaker=name=myCircuitBreaker,fallbackUri=/fallback |
4.1. 路由过滤器
如下是一个简单示例,给所有进入 user-service 的请求添加一个请求头 X-Request-Color=Blue:
YAML
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**
filters:
- AddRequestHeader=X-Request-Color, Blue1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
4.2. 默认过滤器
如果要对所有的路由都生效,则可以将过滤器工厂写到 spring.cloud.gateway.default-filters 下,如下:
YAML
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**
default-filters:
- AddRequestHeader=X-Request-Color, Blue1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
4.3. 全局过滤器
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与 GatewayFilter 的作用一样。区别在于 GatewayFilter 通过配置定义,处理逻辑是固定的。而 GlobalFilter 的逻辑需要自己写代码实现。定义方式是实现 GlobalFilter 接口。
Java
public interface GlobalFilter {
/**
* Process the Web request and (optionally) delegate to the next {@code GatewayFilter}
* through the given {@link GatewayFilterChain}.
* @param exchange the current server exchange
* @param chain provides a way to delegate to the next filter
* @return {@code Mono<Void>} to indicate when request processing is complete
*/
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}如下是一个拦截并判断用户身份过滤器的简单示例:
Java
@Component
public class AuthNFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String authn = exchange.getRequest().getQueryParams().getFirst("authn");
if (!"admin".equals(authn)) {
// 拦截并响应 401 状态码
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 放行
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}警告
记得实现 Ordered 接口。此外 @Order 注解在 Spring Cloud Gateway 3.1.8 版本中无法正常工作,建议检查后续版本是否已修复此问题。
4.4. 过滤器执行顺序
网关路由后,会将当前路由过滤器、默认过滤器、全局过滤器合并到一个过滤器链中,排序后依次执行。
信息
Spring Cloud 内部通过适配器模式将 GlobalFilter 适配为 GatewayFilter:
Java
private static class GatewayFilterAdapter implements GatewayFilter {
private final GlobalFilter delegate;
GatewayFilterAdapter(GlobalFilter delegate) {
this.delegate = delegate;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return this.delegate.filter(exchange, chain);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");
sb.append("delegate=").append(delegate);
sb.append('}');
return sb.toString();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
路由过滤器和默认过滤器的 Order 默认是按照声明顺序从 1 递增的。全局过滤器的 Order 可以通过实现 Ordered 接口指定。过滤器的 Order 值越小,优先级越高,执行顺序越靠前。当过滤器的 Order 值一样时,会按照 “全局过滤器 → 默认过滤器 → 路由过滤器” 的顺序执行。
5. 跨域问题
Spring Cloud Gateway 的参考跨域(CORS)配置示例如下:
YAML
spring:
cloud:
gateway:
globalcors:
add-to-simple-url-handler-mapping: true
cors-configurations:
'[/**]': # 匹配所有路径
allowed-origins: # 允许的源(域名),* 表示允许所有源
- 'http://example.com'
- 'https://example.com'
allowed-methods: # 允许的 HTTP 方法
- GET
- POST
- PUT
- DELETE
- OPTIONS
allowed-headers: '*' # 允许的请求头,* 表示全部
allow-credentials: true # 是否允许携带凭证(如 Cookies)
max-age: 3600 # 预检请求(OPTIONS)的缓存时间(秒)设置 spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping=true 会把全局 CORS 配置应用到 Spring MVC 的 SimpleUrlHandlerMapping 上。这样一来,即使是被 SimpleUrlHandlerMapping 处理的静态资源或 WebFlux 的路由,也能走到全局的 CORS 过滤器,从而支持跨域。
或者对单个微服务进行独立配置:
YAML
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**
metadata:
cors:
allowed-origin-patterns: '*://example.com'
allowed-methods: [GET, POST]
allowed-headers: '*'
allow-credentials: true
max-age: 36006. Actuator
引入依赖
XML<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>配置 Actuator 端点
YAMLmanagement: endpoints: web: exposure: include: gateway访问 Actuator 的 Gateway 端点
以下是几个常用的端点:
/actuator/gateway/globalfilters:列出所有全局过滤器(GlobalFilter)及其配置的 Order 值;/actuator/gateway/routefilters:列出所有路由过滤器工厂(GatewayFilterFactory)的可用实例,这些是可用于路由配置的过滤器模板,而不是实际应用于某条路由的过滤器;/actuator/gateway/routes:列出所有已定义的路由(Route)及其详细信息,包括路由的谓词(Predicates)、过滤器(Filters)和目标 URI;/actuator/gateway/routes/{route_id}:返回指定路由(由route_id标识)的详细信息,包括其谓词、过滤器和目标 URI。