Skip to content

API 网关与服务网格:系统设计里的流量入口层怎么画

本文是系统设计系列 L2 组件积木篇。前置:29. 核心组件手册:LB、缓存、队列与 CDN。 学完可以配合面试题食用:30. 数据层设计19-multi-region-active-active-system

网关在系统设计图里到底画在哪

不少面试者在白板上画架构图时,网关的定位很模糊——画在负载均衡后面、画在某个微服务前面、或者干脆不画。实际上,网关是流量进入业务系统的第一道关口,它夹在 LB 与业务服务之间,职责清晰但边界容易混淆。

先看一张标准分层:

DNS → LB(四层) → API 网关(七层) → BFF(可选) → 业务微服务
  • LB(负载均衡):四层,按 IP+端口分发,不关心请求内容。单机吞吐 10M+ pps,但做不了路由判断。
  • API 网关:七层,解析 HTTP 请求头、路径、Cookie,做路由转发、鉴权、限流、协议转换。吞吐低于 LB,但灵活度远高。
  • BFF(Backend For Frontend):如果客户端有多种(Web/iOS/Android/小程序),BFF 为每种客户端做一次请求裁剪和聚合。BFF 是网关的下游,不是替代品。

网关不该做的事:执行业务逻辑、访问数据库、做重计算。网关是"管道",不是"业务处理器"。如果一个网关里塞了太多业务判断,它就退化成了"单体应用的 Servlet 转发器"。

网关该做什么,不该做什么

职责清单

路由与反向代理:按 path/host/header 把请求分到对应后端服务。这是最基础的功能。

鉴权与身份认证:统一拦截未登录请求,鉴权下发 Token。网关层做一次粗鉴权(Token 是否有效、用户是否封禁),细粒度权限留给业务服务。

限流与熔断:按 API、用户、IP 做限流。网关层的限流是第一道防线,挡住突发的恶意流量(比如爬虫),业务层的限流做精细控制。

灰度发布与流量染色:按 Header/Cookie 把流量切到新版本服务。网关层是灰度路由的天然锚点:不需要改 DNS 或 LB 配置,网关层判断条件即可。

协议转换与请求/响应改写:外部调用是 HTTP/JSON,内部可能是 gRPC/Protobuf,网关做转换。也可以统一加响应头、改写请求路径。

统一日志与监控:网关层能看到所有请求的入口流量,在这里统一采集请求耗时、错误码分布、慢接口追踪,比每个服务各自打日志更完整。

不该做的事

  • 不执行业务逻辑:网关里写 if-else 判断业务分支,意味着每次业务变更都要改网关,网关成了"胖代理"。
  • 不直接访问数据库:网关层做数据查询意味着它跟业务耦合,也失去了通用性。
  • 不做重计算:大数据量聚合、图片处理、文件转换不在网关做,走异步任务或下沉到专用服务。
  • 不做长连接管理:WebSocket 有状态,网关层处理长连接会耗尽连接池,通常直接穿透到业务服务,或由独立网关组件处理。

网关选型:从 Nginx 到 APISIX 的演进

Nginx / OpenResty

Nginx 做网关是最常见的方案。轻量、稳定、单机几万 QPS 没问题。配合 lua-nginx-module(OpenResty)可以写 Lua 脚本做鉴权、限流、灰度。

nginx
# 网关限流 + 灰度路由示例
http {
    # 限流区域:每个 IP 每秒 10 个请求
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    
    # 灰度路由 upstream
    upstream stable_backend {
        server 10.0.1.1:8080 weight=9;
    }
    upstream canary_backend {
        server 10.0.2.1:8080 weight=1;
    }
    
    server {
        listen 80;
        
        location /api/ {
            # 限流
            limit_req zone=api_limit burst=20 nodelay;
            
            # 灰度判断:Header 带 canary=true 走灰度
            set $backend "stable_backend";
            if ($http_x_canary = "true") {
                set $backend "canary_backend";
            }
            
            proxy_pass http://$backend;
            proxy_set_header X-Request-ID $request_id;
        }
        
        location /auth/ {
            # 鉴权转发到 auth 服务
            auth_request /_internal/validate_token;
            proxy_pass http://auth_service;
        }
        
        # 内部鉴权接口
        location = /_internal/validate_token {
            internal;
            proxy_pass http://auth_service/validate;
            proxy_pass_request_body off;
            proxy_set_header Content-Length "";
        }
    }
}

Nginx 做网关的局限:动态路由变更需要 reload,Lua 脚本维护成本高,多租户隔离弱。适合中小规模团队。

Spring Cloud Gateway

Java 技术栈的标配。基于 WebFlux(Reactor)非阻塞,配置路由通过 Java DSL 或 YAML。天然对接 Nacos/Eureka 做服务发现,整合 Sentinel 做限流降级。

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
                key-resolver: "#{@userKeyResolver}"

Spring Cloud Gateway 适合 Java 全家桶团队,但依赖 Spring 生态,gateway 本身的性能瓶颈在 Java 线程模型上(即使 WebFlux 非阻塞,Java 的 GC 停顿和内存占用还是比 Nginx 高一个量级)。

Kong / APISIX

基于 OpenResty 的云原生网关,核心优势是插件化 + 动态配置。Kong 用 Lua 插件,APISIX 已经支持 Lua/Java/Go/Python 多语言插件。通过 admin API 或控制面动态增删路由,不需要 reload。

APISIX 的限流 + 鉴权配置:

yaml
# 通过 Admin API 动态创建路由
curl http://127.0.0.1:9180/apisix/admin/routes/1 -X PUT -d '
{
  "uri": "/api/v1/*",
  "plugins": {
    "limit-count": {
      "count": 100,
      "time_window": 60,
      "rejected_code": 429
    },
    "key-auth": {
      "key": "api-key-xxx"
    },
    "prometheus": {
      "prefer_name": true
    }
  },
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "10.0.1.1:8080": 1,
      "10.0.1.2:8080": 1
    }
  }
}'

Kong/APISIX 适合对动态路由和多租户有要求的场景(API 开放平台、SaaS 平台)。坏处是运维复杂度高——自己维护一个 APISIX 集群,业务量不够大时还不如 Nginx 省心。

云原生网关

云厂商的 API 网关产品(阿里云 API Gateway、AWS API Gateway、腾讯云 API 网关)是托管方案。不需要自己运维,自带鉴权、限流、监控、计费能力。缺点:绑定云厂商,跨云迁移困难,高阶定制受限。

选型口诀:小团队用 Nginx,Java 生态用 SCG,开放平台用 APISIX/Kong,上云用托管

网关与注册中心、配置中心的协作时序

网关不是独立工作的。一个请求从到达网关到转发到后端,实际要跟注册中心和配置中心走一遍:

mermaid
sequenceDiagram
    participant Client as 客户端
    participant GW as API 网关
    participant Registry as 注册中心(Nacos)
    participant Config as 配置中心
    participant Service as 后端服务
    
    Note over GW,Registry: 启动阶段
    GW->>Registry: 1. 订阅服务列表
    Config->>GW: 2. 推送路由规则(灰度/限流配置)
    
    Note over Client,Service: 运行时
    Client->>GW: 3. HTTP 请求
    GW->>GW: 4. 鉴权(Token 校验)
    GW->>GW: 5. 限流检查
    GW->>GW: 6. 灰度判断 → 选择版本
    GW->>Registry: 7. 获取可用实例列表
    GW->>Service: 8. 转发请求(带上灰度标签)
    Service->>GW: 9. 响应
    GW->>Client: 10. 返回结果
    
    Note over GW,Registry: 故障处理
    Service-->>GW: 连续超时/5xx
    GW->>GW: 熔断降级 → 摘除实例
    GW->>Client: 返回降级响应

关键点:网关持有的「服务实例列表」是本地缓存的,不是每次请求都去查注册中心。注册中心通过长连接推送变更,网关收到变更后更新本地缓存。如果注册中心挂了,网关用缓存继续工作,这就是"AP 优于 CP"在网关层的体现。

Service Mesh:网关的下一步演变

网关解决了"入口流量"的问题,但服务之间的调用(东西向流量)还是每个服务自己处理——重试、超时、熔断、mTLS 这些能力,要么每个服务重复实现,要么依赖库的方式引入。Service Mesh 把这些问题从应用层下沉到基础设施层

Istio + Envoy 是当前最主流的 Service Mesh 方案。每个服务部署一个 sidecar proxy(Envoy),所有进出流量都经过 sidecar。控制面(Istiod)下发配置,数据面(Envoy)执行路由、熔断、重试、观测。

网关 + Sidecar 的分层

外部流量 → 入口网关(Istio Ingress Gateway) → sidecar → 服务 A → sidecar → 服务 B

入口网关跟 API 网关的职责类似,但由 Istio 统一管理——路由规则、mTLS 证书、流量切分全部通过 K8s CRD 声明式配置。

yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: user-service
spec:
  hosts:
  - user-service
  http:
  - match:
    - headers:
        x-canary:
          exact: "true"
    route:
    - destination:
        host: user-service
        subset: v2
      weight: 100
  - route:
    - destination:
        host: user-service
        subset: v1
      weight: 90
    - destination:
        host: user-service
        subset: v2
      weight: 10
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: user-service-circuit-breaker
spec:
  host: user-service
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        http1MaxPendingRequests: 10
        maxRequestsPerConnection: 10
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 30s
      baseEjectionTime: 30s

这个配置做了三件事:按 Header 分流(灰度)、连接池限制、熔断检测。全部在 Istio 层面完成,应用代码一行都不用改。

Service Mesh 引入后,API 网关的职责怎么变

网关的熔断、重试、mTLS 能力下沉到 sidecar 后,网关可以更"薄":

  • 网关负责:外部鉴权、限流、协议转换、路由分发
  • Sidecar 负责:服务间熔断、重试、超时、mTLS、分布式追踪

网关不再需要操心"后端服务之间怎么调",只关心"外部请求怎么进入内网"。

要不要上 Service Mesh?看三点:有没有多语言服务(Java/Go/Node 混布,统一 mTLS 和熔断逻辑)、流量治理需求是否频繁变更(CRD 配路由比改代码重启快)、团队有没有 K8s 运维能力(Service Mesh 的运维复杂度是网关的 3-5 倍,小团队慎入)。

什么规模不需要网关

日活 10 万以下、微服务少于 5 个的系统,不需要独立的 API 网关层

少于 5 个服务时,Nginx 直接做反向代理就够了。路由配置写在 nginx.conf 里,鉴权在业务服务的 filter 里做,所谓的"统一入口"需求其实没那么急迫。

网关是"解决分布式复杂性的工具",不是"分布式系统标配"。在架构图上画网关之前,先问一个问题:不加网关,哪里会痛? 答案越具体,网关越需要加;答案越模糊,说明还不需要。

参考

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。
粤ICP备2026104257号-1