API 网关
API 网关是微服务架构的重要组件,作为系统的统一入口,负责请求路由、协议转换、认证授权、限流熔断等工作。
为什么需要 API 网关
理解网关的价值,需要先看看没有网关时的架构问题。
客户端直接调用的问题
在微服务架构中,服务数量众多。如果客户端直接调用各个服务,会面临以下问题:
客户端需要知道每个服务的地址,服务地址变更时客户端需要修改。每个服务可能部署多个实例,客户端需要自己实现负载均衡。每个服务可能需要独立的认证,客户端需要重复实现认证逻辑。跨域问题、协议转换等横切关注点分散在各个服务中。
网关的作用
API 网关作为系统的统一入口,解决了上述问题:
统一路由:客户端只需要知道网关地址,网关根据路径将请求转发到对应服务。负载均衡:网关集成负载均衡能力,自动分发请求到服务实例。认证授权:网关统一处理认证授权,后端服务无需关心。协议转换:网关可以处理 HTTP、WebSocket 等协议的转换。限流熔断:网关可以在入口处实现限流和熔断,保护后端服务。日志监控:网关统一记录请求日志,便于监控和问题排查。
Spring Cloud Gateway 简介
Spring Cloud Gateway 是 Spring 官方开发的 API 网关,基于 Spring Framework 6、Spring Boot 3 和 Project Reactor 构建。它是 Zuul 的替代方案,提供了更好的性能和更丰富的功能。
核心概念
Spring Cloud Gateway 有三个核心概念:
Route(路由)是网关的基本构建块,由 ID、目标 URI、断言和过滤器组成。当断言为真时,路由匹配成功。
Predicate(断言)用于匹配 HTTP 请求的各个方面,如路径、请求头、请求参数、时间等。Spring Cloud Gateway 提供了丰富的内置断言。
Filter(过滤器)用于在请求发送到下游服务之前或之后修改请求和响应。可以在路由级别配置,也可以全局配置。
工作原理
请求到达网关后的处理流程:
- DispatcherHandler 接收请求
- RoutePredicateHandlerMapping 根据断言匹配路由
- FilteringWebHandler 执行过滤器链
- 过滤器链中的过滤器依次执行前置逻辑
- 请求转发到下游服务
- 过滤器链依次执行后置逻辑
- 响应返回给客户端
快速开始
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
如果需要服务发现功能:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
基础配置
server:
port: 8080
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
lb:// 前缀表示使用 LoadBalancer 进行负载均衡,后面的 user-service 是服务名。网关会从注册中心获取该服务的实例列表并负载均衡。
动态路由
开启服务发现的路由定位器,可以自动根据服务名创建路由:
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
开启后,可以通过 /服务名/路径 的方式访问服务,如 /user-service/user/1。
路由断言详解
Spring Cloud Gateway 提供了丰富的内置断言,可以组合使用。
Path 路径断言
最常用的断言,根据请求路径匹配:
predicates:
- Path=/user/** # 匹配 /user 开头的路径
- Path=/user/{id} # 匹配 /user/数字 的路径,并提取变量
- Path=/user/{id},/order/** # 多个路径
路径变量可以在过滤器中使用:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/user/{segment}
filters:
- SetPath=/user/{segment} # 重写路径
Method 方法断言
根据 HTTP 方法匹配:
predicates:
- Method=GET
- Method=GET,POST,PUT # 多个方法
Header 请求头断言
根据请求头匹配:
predicates:
- Header=X-Request-Id # 存在该请求头
- Header=X-Request-Id, \d+ # 请求头值匹配正则
- Header=Content-Type, application/json
Query 查询参数断言
根据查询参数匹配:
predicates:
- Query=name # 存在该查询参数
- Query=name, zhang.* # 参数值匹配正则
时间断言
根据请求时间匹配,常用于灰度发布:
predicates:
- After=2024-01-01T00:00:00+08:00[Asia/Shanghai]
- Before=2024-12-31T23:59:59+08:00[Asia/Shanghai]
- Between=2024-01-01T00:00:00+08:00[Asia/Shanghai], 2024-12-31T23:59:59+08:00[Asia/Shanghai]
Cookie 断言
根据 Cookie 匹配:
predicates:
- Cookie=session # 存在该 Cookie
- Cookie=session, abc123 # Cookie 值匹配正则
Host 主机断言
根据 Host 请求头匹配:
predicates:
- Host=**.example.com
- Host=api.example.com,**.test.example.com
RemoteAddr 远程地址断言
根据客户端 IP 匹配:
predicates:
- RemoteAddr=192.168.1.1/24
- RemoteAddr=10.0.0.0/8,192.168.0.0/16
Weight 权重断言
按权重分流,常用于灰度发布:
routes:
- id: user-service-v1
uri: lb://user-service-v1
predicates:
- Path=/user/**
- Weight=group1, 80
- id: user-service-v2
uri: lb://user-service-v2
predicates:
- Path=/user/**
- Weight=group1, 20
同一 group1 组内,80% 请求到 v1,20% 请求到 v2。
组合断言
多个断言默认是 AND 关系。可以通过配置实现 OR 关系:
routes:
- id: example
uri: lb://example-service
predicates:
- name: Path
args:
patterns: /api/**
- name: Method
args:
methods: GET,POST
过滤器详解
Spring Cloud Gateway 提供了丰富的内置过滤器,按功能分为请求头处理、路径处理、请求参数处理等。
请求头过滤器
添加请求头:
filters:
- AddRequestHeader=X-Request-Id, 12345
- AddRequestHeader=X-Source, gateway
添加响应头:
filters:
- AddResponseHeader=X-Response-Time, 2024-01-01
移除请求头:
filters:
- RemoveRequestHeader=X-Unwanted-Header
移除响应头:
filters:
- RemoveResponseHeader=X-Unwanted-Header
设置请求头(覆盖已存在的):
filters:
- SetRequestHeader=X-Request-Id, 54321
路径过滤器
去除路径前缀:
filters:
- StripPrefix=1 # /api/user/1 -> /user/1
- StripPrefix=2 # /api/v1/user/1 -> /user/1
添加路径前缀:
filters:
- PrefixPath=/api # /user/1 -> /api/user/1
重写路径:
filters:
- RewritePath=/api/(?<segment>.*), /$\{segment}
# /api/user/1 -> /user/1
设置路径:
filters:
- SetPath=/api/user/{segment}
请求参数过滤器
添加请求参数:
filters:
- AddRequestParameter=foo, bar
移除请求参数:
filters:
- RemoveRequestParameter=unwanted
状态码过滤器
设置响应状态码:
filters:
- SetStatus=200
- SetStatus=NOT_FOUND
重定向过滤器
重定向:
filters:
- RedirectTo=302, https://example.com
重试过滤器
自动重试失败的请求:
filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY,SERVICE_UNAVAILABLE
methods: GET
backoff:
firstBackoff: 100ms
maxBackoff: 500ms
factor: 2
请求大小限制
限制请求体大小:
filters:
- name: RequestSize
args:
maxSize: 500KB
熔断过滤器
集成 Resilience4j 实现熔断:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
filters:
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/fallback
降级处理:
@RestController
public class FallbackController {
@GetMapping("/fallback")
public Mono<Map<String, Object>> fallback() {
Map<String, Object> result = new HashMap<>();
result.put("code", 503);
result.put("message", "服务暂时不可用");
return Mono.just(result);
}
}
限流过滤器
集成 Redis 实现限流:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 每秒补充令牌数
redis-rate-limiter.burstCapacity: 20 # 令牌桶容量
key-resolver: "#{@userKeyResolver}" # 限流键解析器
@Configuration
public class GatewayConfig {
@Bean
public KeyResolver userKeyResolver() {
// 按用户 ID 限流
return exchange -> Mono.just(
exchange.getRequest()
.getQueryParams()
.getFirst("userId")
);
}
@Bean
public KeyResolver ipKeyResolver() {
// 按 IP 限流
return exchange -> Mono.just(
exchange.getRequest()
.getRemoteAddress()
.getAddress()
.getHostAddress()
);
}
}
自定义过滤器
当内置过滤器不能满足需求时,可以实现自定义过滤器。
全局过滤器
全局过滤器对所有路由生效:
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
// 排除不需要认证的路径
if (path.startsWith("/auth/") || path.equals("/health")) {
return chain.filter(exchange);
}
// 获取 Token
String token = request.getHeaders().getFirst("Authorization");
if (token == null || !validateToken(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 解析用户信息并添加到请求头
String userId = parseUserId(token);
ServerHttpRequest newRequest = request.mutate()
.header("X-User-Id", userId)
.build();
return chain.filter(exchange.mutate().request(newRequest).build());
}
@Override
public int getOrder() {
return -100; // 数字越小,优先级越高
}
private boolean validateToken(String token) {
// 验证 Token 逻辑
return true;
}
private String parseUserId(String token) {
// 解析用户 ID
return "user123";
}
}
网关过滤器工厂
创建可配置的局部过滤器:
@Component
public class RequestTimeGatewayFilterFactory
extends AbstractGatewayFilterFactory<RequestTimeGatewayFilterFactory.Config> {
public RequestTimeGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
long startTime = System.currentTimeMillis();
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
if (config.isLogRequestTime()) {
log.info("请求 {} 耗时 {}ms",
exchange.getRequest().getURI(), duration);
}
// 添加响应头
exchange.getResponse().getHeaders()
.add("X-Response-Time", String.valueOf(duration));
})
);
};
}
public static class Config {
private boolean logRequestTime = true;
public boolean isLogRequestTime() {
return logRequestTime;
}
public void setLogRequestTime(boolean logRequestTime) {
this.logRequestTime = logRequestTime;
}
}
}
使用:
filters:
- name: RequestTime
args:
logRequestTime: true
跨域配置
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
// 允许的域名
config.addAllowedOriginPattern("*");
// 允许的方法
config.addAllowedMethod("*");
// 允许的头
config.addAllowedHeader("*");
// 允许携带凭证
config.setAllowCredentials(true);
// 预检请求缓存时间
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
高可用部署
网关作为系统入口,需要保证高可用。
多实例部署
部署多个网关实例,通过负载均衡器(如 Nginx、云厂商 LB)分发请求:
客户端 -> 负载均衡器 -> 网关实例 1
-> 网关实例 2
-> 网关实例 3
健康检查
配置健康检查,自动剔除故障实例:
management:
endpoints:
web:
exposure:
include: health
endpoint:
health:
show-details: always
性能调优
连接池配置
网关使用 Netty 作为 HTTP 客户端,可以配置连接池:
spring:
cloud:
gateway:
httpclient:
pool:
type: elastic
max-idle-time: 15000
connect-timeout: 3000
response-timeout: 30000
内存配置
调整 Netty 内存配置:
server:
netty:
connection-timeout: 5000
idle-timeout: 60000
响应式编程注意事项
Spring Cloud Gateway 基于响应式编程,需要注意:
不要在过滤器中阻塞操作,如 Thread.sleep()、JDBC 调用。必须阻塞时,应该使用 subscribeOn 指定调度器。避免在过滤器中创建大量对象,可能触发频繁 GC。
最佳实践
路由设计
路由粒度适中,太粗难以精细化控制,太细维护成本高。按服务名作为路径前缀,便于管理和监控。统一 API 版本管理,如 /v1/user/、/v2/user/。
安全设计
敏感路径必须认证,开放路径白名单管理。不要在网关做复杂的权限校验,权限下沉到各服务。Token 解析在网关完成,用户信息透传给下游服务。
限流设计
根据服务能力配置限流阈值。区分不同接口的限流策略,核心接口更宽松。限流触发时返回友好提示,包含重试建议。
监控告警
监控网关的 QPS、响应时间、错误率。监控下游服务的健康状态。配置告警,异常时及时通知。
小结
本章详细介绍了 Spring Cloud Gateway:
API 网关作为系统统一入口,处理路由、认证、限流等横切关注点。Spring Cloud Gateway 是 Spring 官方的网关方案,基于响应式编程。断言用于匹配请求,过滤器用于处理请求和响应。支持熔断、限流、重试等高级特性。注意高可用部署和性能调优。