第 11 章:API 网关
学习目标
- 理解 API 网关的角色与价值
- 掌握 Spring Cloud Gateway 路由配置
- 学会过滤器、限流、鉴权
- 避免网关性能瓶颈和单点故障
一、API 网关是什么?
text
客户端
└── 浏览器/小程序/APP
↓
[API 网关] ← 统一入口
├── 路由
├── 鉴权
├── 限流
├── 熔断
├── 日志
└── 协议转换
↓
┌────┴────┬────┐
[订单] [支付] [商品] ← 内部服务没有网关的话,客户端要记住每个服务地址:
java
// ❌ 客户端直连
const orderApi = "http://order.internal:8080/api/order";
const payApi = "http://payment.internal:8081/api/payment";
const productApi = "http://product.internal:8082/api/product";
// 内部 IP 暴露、服务迁移要客户端改代码二、Spring Cloud Gateway
yaml
# application.yml
spring:
cloud:
gateway:
routes:
- id: order_route
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- StripPrefix=2
- id: payment_route
uri: lb://payment-service
predicates:
- Path=/api/payment/**
filters:
- StripPrefix=2
- id: product_route
uri: lb://product-service
predicates:
- Path=/api/product/**
- Method=GETjava
// 路由配置也可以用代码
@Bean
public RouteLocator customRoute(RouteLocatorBuilder builder) {
return builder.routes()
.route("order_route", r -> r
.path("/api/order/**")
.filters(f -> f.stripPrefix(2).addRequestHeader("X-Source", "gateway"))
.uri("lb://order-service"))
.build();
}三、过滤器(Filter)
java
// 全局过滤器:鉴权
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 校验 token,提取用户信息
String userId = parseUserId(token);
// 传递给下游服务
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-User-Id", userId)
.build();
return chain.filter(exchange.mutate().request(request).build());
}
@Override
public int getOrder() {
return -100; // 优先级
}
}java
// 局部过滤器:去掉前缀
@Bean
public RouteLocator prefixRoute(RouteLocatorBuilder builder) {
return builder.routes()
.route("order_route", r -> r
.path("/api/order/**")
.filters(f -> f.stripPrefix(2)) // /api/order/list → /list
.uri("lb://order-service"))
.build();
}⚠️ 坑 1:过滤器里阻塞 IO(
Thread.sleep、同步 HTTP 调用) = 拖垮整个网关。用响应式 API(Mono/Flux)。
四、限流
yaml
# 基于 Redis 的令牌桶
spring:
cloud:
gateway:
routes:
- id: order_route
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 100
redis-rate-limiter.burstCapacity: 200
key-resolver: "#{@userKeyResolver}"java
// KeyResolver:按用户 ID 限流
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getHeaders().getFirst("X-User-Id")
);
}五、熔断
yaml
spring:
cloud:
gateway:
routes:
- id: order_route
uri: lb://order-service
filters:
- name: CircuitBreaker
args:
name: orderCircuitBreaker
fallbackUri: forward:/fallback/orderjava
// Fallback 处理器
@RestController
public class FallbackController {
@RequestMapping("/fallback/order")
public Map<String, Object> orderFallback() {
Map<String, Object> result = new HashMap<>();
result.put("code", 503);
result.put("message", "订单服务暂时不可用,请稍后再试");
return result;
}
}六、CORS 跨域
java
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.setAllowCredentials(true);
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}⚠️ 坑 2:
addAllowedOrigin("*")和setAllowCredentials(true)不能同时设置,否则浏览器拒绝。用addAllowedOriginPattern。
七、网关高可用
text
生产部署
[Nginx]
↓
[Gateway 1] [Gateway 2] [Gateway 3] ← 至少 2 个实例
↓
[Nacos]yaml
# Gateway 自身注册到 Nacos
spring:
cloud:
nacos:
discovery:
server-addr: nacos:8848nginx
# Nginx upstream
upstream gateway {
server gateway1:8080;
server gateway2:8081;
server gateway3:8082;
keepalive 32;
}
server {
listen 80;
location / {
proxy_pass http://gateway;
}
}八、网关性能优化
yaml
# 1. 启用响应式
spring:
webflux:
base-packages: com.example
# 2. 调整 Tomcat 参数(reactive)
server:
netty:
connection-timeout: 5s
idle-timeout: 60sjava
// 3. 减少过滤器链路
// 鉴权 → 限流 → 业务,固定顺序,不要串 10 个过滤器
// 4. 缓存热点数据
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("userInfo"); // 网关层缓存用户信息
}⚠️ 坑 3:网关处理所有流量,数据库查询放网关 = 把数据库打穿。网关只做转发,业务逻辑放下游服务。
本章小结
| 功能 | 做法 |
|---|---|
| 路由 | Path + lb://service,支持动态配置 |
| 鉴权 | 全局过滤器,校验 token,传递用户信息 |
| 限流 | RequestRateLimiter + Redis |
| 熔断 | CircuitBreaker + Fallback |
| CORS | CorsWebFilter |
| 高可用 | 至少 2 实例 + Nginx upstream |
动手练习
- 路由配置:写 3 个路由,分别转发到 order/payment/product 服务
- 鉴权过滤器:写
AuthFilter,校验 JWT 后在 Header 传递X-User-Id - 限流:每个用户 100 QPS,观察超出后请求被拒绝
- Fallback:故意停掉 payment 服务,触发熔断,返回友好提示
下一章:第 12 章:分布式锁 →