Skip to content
第 11 章 架构 ⏱ 12 分钟阅读

第 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=GET
java
// 路由配置也可以用代码
@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/order
java
// 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:8848
nginx
# 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: 60s
java
// 3. 减少过滤器链路
// 鉴权 → 限流 → 业务,固定顺序,不要串 10 个过滤器

// 4. 缓存热点数据
@Bean
public CacheManager cacheManager() {
    return new ConcurrentMapCacheManager("userInfo");  // 网关层缓存用户信息
}

⚠️ 坑 3:网关处理所有流量,数据库查询放网关 = 把数据库打穿。网关只做转发,业务逻辑放下游服务。

本章小结 ​

功能做法
路由Path + lb://service,支持动态配置
鉴权全局过滤器,校验 token,传递用户信息
限流RequestRateLimiter + Redis
熔断CircuitBreaker + Fallback
CORSCorsWebFilter
高可用至少 2 实例 + Nginx upstream

动手练习 ​

  1. 路由配置:写 3 个路由,分别转发到 order/payment/product 服务
  2. 鉴权过滤器:写 AuthFilter,校验 JWT 后在 Header 传递 X-User-Id
  3. 限流:每个用户 100 QPS,观察超出后请求被拒绝
  4. Fallback:故意停掉 payment 服务,触发熔断,返回友好提示

下一章:第 12 章:分布式锁 →

本站基于 VitePress 构建 · 由 StackHub 团队维护