微服务架构里,几十上百个服务各有各的地址、协议与鉴权方式,客户端直接连会让「服务发现、安全、限流、灰度」散落到每个服务里。API 网关把所有外部流量收敛到一个统一入口,承担路由转发、鉴权、限流、熔断、灰度与可观测等横切职责,让后端服务专注于业务本身。本文按照系统设计面试的标准答题结构,设计一个生产级的 API 网关系统。
一句话:API 网关是「所有流量的前门」——把「每个服务都要做的事」集中到一处做一遍,用配置而非改代码驱动路由与策略,同时保证自身不成为新的瓶颈与单点。
一、需求澄清与量级估算
1.1 需求澄清
面试官给出题目「设计一个 API 网关系统」后,先通过提问明确边界:
- 流量来源:移动 App、H5、开放平台第三方、内部服务间,覆盖哪些?
- 协议:HTTP/HTTPS、WebSocket、gRPC,是否需要协议转换?
- 路由能力:按路径/域名/Host 路由?是否需要动态上线新服务?
- 安全职责:鉴权(JWT/OAuth)、签名校验、IP 白名单、防刷,哪些必需?
- 治理能力:限流、熔断、降级、灰度发布、请求日志,是否需要?
- 性能要求:网关增加的额外时延上限?峰值 QPS?
- 高可用:网关自身如何避免单点?
明确假设(面向面试的合理假设):
| 需求项 | 假设 |
|---|---|
| 流量来源 | App + H5 + 第三方开放平台 |
| 协议 | HTTP/HTTPS 为主,少量 WebSocket |
| 路由 | 动态路由,服务上线自动生效 |
| 安全 | JWT 鉴权 + 签名校验 + IP 黑白名单 |
| 治理 | 限流 + 熔断 + 灰度 + 全量日志 |
| 性能 | 网关额外时延 < 1ms,峰值 100 万 QPS |
| 高可用 | 多机房多副本,故障自动摘除 |
1.2 量级估算
| 指标 | 估算值 | 推导 |
|---|---|---|
| 峰值 QPS | 100 万 | 全部外部流量收敛入口 |
| 网关节点 | 500 台 | 无状态水平扩展 |
| 后端服务 | 200 个 | 微服务规模 |
| 路由条目 | 1 万条 | 服务 × 接口 × 版本 |
| 额外时延 | P99 < 1ms | 网关只做转发与轻策略 |
| 日志量 | 日千亿条 | 全量访问日志 |
一句话:100 万 QPS、每个请求都要过一遍「路由 + 鉴权 + 限流」——网关必须是「无状态、内存路由、旁路策略」的组合,任何一步去查数据库都会把延迟打穿。
二、高层架构设计
┌──────────────────────────┐ ┌──────────────────────────┐
│ 客户端 (App/H5/第三方) │ │ 运维/开发 (配置路由/策略) │
└────────────┬─────────────┘ └────────────┬─────────────┘
│ API 请求(HTTPS) │ 配置/发布
▼ ▼
┌─────────────────────────────────────────────────────────┐
│ 网关数据面 (Gateway Data Plane) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 协议解析 │ │ 鉴权认证 │ │ 路由转发 │ │ 限流熔断 │ │
│ │ 解/加密 │ │ JWT/签名 │ │ 负载均衡 │ │ 降级重试 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ 无状态集群,水平扩展,全内存处理热路径 │
└──────┬──────────────────────────────────────────────────┘
│ 转发到后端
▼
┌─────────────────────────────────────────────────────────┐
│ 后端服务集群 (200 个服务) │
│ 服务A (v1/v2) 服务B 服务C ... 服务N │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 网关控制面 (Gateway Control Plane) │
│ 路由/策略配置中心 → 推送到数据面各节点 │
│ 服务注册发现(Nacos/Consul) → 动态路由表 │
│ 监控指标(时延/错误率/QPS) 告警 日志 │
└─────────────────────────────────────────────────────────┘
整体拆为两部分:
- 数据面:无状态转发集群,执行路由、鉴权、限流、熔断,全内存热路径。
- 控制面:配置中心 + 服务注册发现 + 监控,把路由与策略下发到数据面。
2.1 控制面与数据面分离
网关的高可用建立在「数据面无状态、控制面可降级」上:
数据面:只管转发与执行策略,不持有可写状态
- 路由表、限流配额等配置从控制面拉取,本地缓存一份
- 控制面故障 → 数据面用本地缓存继续工作(策略保守化)
控制面:管配置下发与服务发现
- 配置变更 → 秒级推送到全部数据面节点
- 路由表快照版本化,节点原子切换
好处:
- 数据面任意扩缩容,加机器即加吞吐
- 控制面短暂不可用,线上流量不受影响
一句话:把「决策配置」与「执行流量」拆成控制面和数据面,数据面无状态可水平扩、控制面挂了也能靠本地缓存撑着——网关自己才不会成为单点。
三、核心组件设计
3.1 动态路由与负载均衡
路由是网关的第一职责:请求 → 哪个服务 → 哪个实例:
路由匹配(从高优先级到低优先级):
① 域名/Host → 服务组
② 路径前缀 → 具体服务:/api/v1/user/** → user-service
③ 请求头/参数 → 灰度分组(canary 标签)
④ 版本:/api/v2/order → order-service v2
服务发现:
服务实例注册到 Nacos/Consul(含健康状态)
网关订阅服务列表 → 构建本地路由表(服务 → 健康实例列表)
实例下线/不健康 → 路由表剔除,秒级生效
负载均衡策略:
加权轮询(按实例容量权重)
最少连接 / 一致性哈希(按用户 id 哈希固定路由,利于会话保持)
;; 伪代码:路由表构建与匹配
(defn build-route-table [services]
;; 服务注册信息 → {prefix → [{service, version, nodes[]}]}
(group-by-path services))
(defn route-request [req route-table]
(let [match (longest-prefix-match (:path req) route-table)
node (pick-node match) ; 加权轮询/哈希
node] ; 返回目标实例))
;; 实例健康感知
(defn mark-unhealthy [node]
(remove-node! route-table node)) ; 剔除故障实例
要点:路由是「配置驱动」而非「改代码驱动」——新服务上线注册即路由生效,旧服务下线即摘除;路由匹配用最长前缀 + 优先级链,让特殊规则(灰度)优先于通用规则。
3.2 鉴权与认证
网关是鉴权的天然位置——一次鉴权,所有后端服务免鉴权:
鉴权模型:
JWT(无状态):
客户端登录后拿 token(含 uid/角色/过期时间,签名保证不可篡改)
网关验签 + 验过期 + 验角色 → 通过则透传用户身份头
无状态:网关无需查库,验签即可
缺点:无法立即撤销 → 用黑名单兜底
签名校验(开放平台):
第三方请求带 appKey + 时间戳 + 签名(MD5/HMAC)
网关用 appKey 对应的密钥重算签名比对
防重放:时间戳窗口(5 分钟)+ nonce 缓存
多租户隔离:
按 appKey/租户 id 隔离路由与配额
租户级黑名单、租户级限流
;; 伪代码:JWT 校验
(defn verify-jwt [token]
(when-let [claims (jwt/verify token secret)] ; 验签名
(when (> (:exp claims) (now))
(when (not (blacklisted? (:jti claims))) ; 撤销检查
claims))))
;; 通过后把身份注入转发请求头
(if-let [claims (verify-jwt token)]
(forward! (assoc req :header-user claims))
(resp 401 "unauthorized"))
一句话:网关把鉴权收敛成「一次验签、一份信任」——后端服务默认信任网关注入的用户身份,各自不用再写鉴权代码;JWT 保无状态,签名校验保开放平台安全,租户隔离保多租户公平。
3.3 限流与熔断
网关承载全部流量,保护后端是它的核心价值:
限流(防打爆):
全局维度:网关总 QPS 上限
服务维度:每个后端服务的调用配额
用户/租户维度:单用户单接口频率
实现:令牌桶/滑动窗口,配额存 Redis 或本地分片计数
熔断(防雪崩):
监控后端错误率/时延:连续失败超过阈值 → 打开熔断器
熔断期间直接返回降级响应(不把流量打到已故障的服务)
半开探测:过一段冷却时间放少量流量试探,恢复则关闭熔断
降级:
超时快速失败(默认 3s)→ 返回缓存数据或友好提示
重试:只对幂等请求重试一次,避免重试风暴
;; 伪代码:服务级熔断
(defn should-trip? [service]
(let [stats (circuit-stats service)]
(or (> (:error-rate stats) 0.5) ; 错误率 > 50%
(> (:p99-latency stats) 3000)))) ; P99 > 3s
(defn call-backend [req service]
(cond
(circuit-open? service) (degraded-resp service) ; 熔断降级
(exceed-quota? service) (resp 429 "too many")
:else (proxy-forward req service)))
要点:限流是「进门前限量」,熔断是「出门遇险关门」——限流挡住超量请求保护所有服务,熔断在单个服务故障时快速止损并降级,二者配合才避免「一个服务慢拖垮全站」的雪崩。
3.4 灰度发布
新版本不能全量上线,网关是灰度执行的绝佳位置:
灰度方案:
版本路由:/api/v2/** 只路由到新版本实例,v1 继续服务老流量
流量灰度:按用户 id/地域/设备,把 x% 流量路由到新版本
标签路由:请求带 canary 头 → 网关命中灰度分组
逐步放量:5% → 20% → 50% → 100%,每步观察监控
回滚:
监控异常 → 切回旧版本路由(配置秒级生效)
灰度实例摘除,流量全回 v1
配置示例(灰度规则):
route /api/order/**:
version: v1
weight: 80
gray:
version: v2
weight: 20
rule: uid_hash % 100 < 20 # 20% 用户走 v2
一句话:灰度发布把「上线」从「开关切换」变成「比例调节」——网关按权重和规则把流量分给新旧版本,监控无异常再逐步放量,出问题秒级回滚,发布风险从「事故」变成「可预期的小波动」。
3.5 性能与高可用
网关自己必须是「又快又稳」,否则全站受害:
性能设计:
全异步 IO(Netty/异步 HTTP),避免线程池阻塞
热路径只读内存路由表,不查库、不跨机 RPC
连接池与 TLS 会话复用,减少握手开销
响应压缩(gzip/br),减小带宽
高可用:
多机房多副本部署,DNS/LB 就近接入
数据面无状态:机器故障自动摘除,流量由健康节点承接
配置中心多副本,路由表本地缓存兜底
全链路压测:验证网关在峰值 + 故障叠加下的表现
| 手段 | 解决的问题 |
|---|---|
| 全异步 IO | 高并发下的线程阻塞 |
| 内存路由 + 本地缓存 | 控制面故障不中断转发 |
| 多机房多副本 | 单机房故障不挂 |
| 连接池/TLS 复用 | 降低时延与握手开销 |
| 全链路压测 | 提前暴露容量与雪崩点 |
结论:网关的性能红线是「自身时延 < 1ms」——用异步 IO + 内存热路径做到;高可用红线是「自身不成为单点」——用无状态多副本 + 本地缓存兜底做到;两条红线都守住,网关才能放心地承担「所有流量的前门」。
四、深入权衡
4.1 网关聚合层数
| 方案 | 优点 | 缺点 |
|---|---|---|
| 单层网关 | 简单,一个入口 | 路由/策略耦合,扩展受限 |
| 双层(入口 + 业务网关) | 入口管安全,业务网关管路由 | 多一跳延迟 |
| 服务网格(Sidecar) | 治理下沉到边车,应用无感 | 复杂度高,运维重 |
结论:主流是「入口网关 + 服务网格/业务网关」两层——入口管全局安全与流量,业务网关管服务路由与治理;层数每加一层都加延迟,够用即可。
4.2 限流状态放本地还是集中
本地限流:每节点独立配额,零延迟,但总量不准(节点数 × 单节点)
集中限流:Redis 计数,总量精确,但每个请求多一次 RTT,且 Redis 是热点
折中:本地分片配额 + 周期性与集中计数校准(微误差可接受)
一句话:限流精度与延迟不可兼得——生产上用「本地配额为主、Redis 校准兜底」的混合方案,让限流既准又不成为性能瓶颈。
4.3 网关承载业务逻辑吗
网关容易「什么都往里塞」导致膨胀变慢。权衡原则:
适合放网关的:横切能力(鉴权/限流/熔断/灰度/日志/协议转换)
不适合放网关的:业务逻辑(算价/库存/推荐规则)
结论:网关只做「所有服务都要做的事」,业务逻辑留在各自服务——保持网关「薄而快」,是它长期不腐化的关键纪律。
五、总结
API 网关系统的骨架是「控制面与数据面分离」:数据面是无状态的转发集群,用内存路由表承载路由、鉴权、限流、熔断、灰度等横切职责,额外时延控制在毫秒内;控制面负责配置下发与服务发现,故障时可被数据面的本地缓存兜底。三个核心工程决策是:一是配置驱动,新服务上线注册即路由、灰度放量即改权重,运维不需要改代码;二是前移治理,鉴权限流熔断全部收敛到网关,后端服务只管业务;三是性能与高可用双红线,用异步 IO 与内存热路径保证快,用多副本与本地缓存保证稳。最终,网关以「一个入口、一套策略、处处生效」的姿态,成为微服务架构里安全、稳定、高效的流量总闸。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。