API 网关架构图:路由、认证、限流与 BFF

一张完整的 API 网关架构图:网关该做什么、不该做什么,令牌校验,基于 429 和 Retry-After 的限流,以及 BFF。

· 创始人兼开发者

发布于

API 网关架构图:路由、认证、限流与 BFF

API 网关是每张架构图里都有、却很少有人解释的那个方框。它位于系统边缘,随着时间推移不断揽下职责:路由、认证、限流、请求改写,有时甚至还有它本不该承担的业务规则。如果图上只画了一支穿过网关图标的箭头,就没人知道它到底承担了其中哪些职责,这些问题会在事故中冒出来,而不是在评审时。

本文为一种常见架构绘制 API 网关架构图:一个 Web 应用、若干移动应用和合作伙伴都位于同一个网关之后,并且每个前端都有自己的后端。文章说明网关该做什么、不该做什么,令牌校验和限流如何工作,以及应该标注什么,才能让这张图回答真实的问题。

哪些职责属于网关

职责 放在网关? 原因
TLS 终止 是 证书集中在一个地方管理
按路径或主机路由 是 客户端只看到一个入口
令牌校验 是 无效请求在到达服务之前就被拒绝
粗粒度授权,例如 scope 通常是 没有 orders:read 的合作伙伴令牌永远到不了 Orders
细粒度授权 否 只有服务知道这个用户能否取消这笔订单
限流与配额 是 保护服务,并执行合作伙伴的套餐限制
请求 ID、访问日志、链路追踪 是 对边缘有统一一致的视图
为某个页面聚合调用 放在 BFF 把界面专属的逻辑挡在共享网关之外
业务规则 否 它们属于服务,在那里才能被测试

负载均衡器把请求分发到各个实例上,但它不知道调用方是谁。服务网格处理系统内部服务之间的流量。网关处理从外部进入的流量,并且了解客户端:它们的令牌、路由和配额。很多系统三者都在用。

在边缘校验令牌

在示例中,客户端通过身份提供商登录,并在每个请求中携带一个 JSON Web Token(RFC 7519)。网关以 JWKS 文档的形式获取并缓存提供商的公钥,用它校验签名,然后检查各项声明:签发者(iss)、受众(aud)和过期时间(exp)。

这里值得遵循 RFC 8725 中的 JWT 当前最佳实践。函数库必须允许你指定允许的算法,并且不得使用其他算法,所以要配置允许列表,而不是信任令牌头部。当同一个签发者为多个应用签发令牌时,令牌必须携带受众,而且每个应用都必须检查受众是否是自己。

有效的令牌说明调用方是谁,但不说明对方能做什么。网关可以拒绝缺少 scope 的请求,但这个用户能否修改这笔订单,仍然由服务来决定。

限流

按客户端计数,而不是按 IP 地址。 在 NAT 之后,许多用户可能共用一个地址,而一个爬虫也可能使用许多地址。用 API 密钥、OAuth 客户端或用户作为限流的键。

共享计数器。 如果有多个网关实例且各自计数,每分钟 100 次的限制就会变成每个实例 100 次。Redis 这样的共享存储能在所有实例之间为每个客户端维护同一个计数。

选定算法并写下来。 固定窗口每分钟重置一次,会在窗口边界处允许突发流量。滑动窗口可以把它平滑掉。令牌桶在保持平均速率的同时,允许在设定大小以内的短时突发。

用 429 响应。 RFC 6585 定义了 429 Too Many Requests。响应应当说明原因,并且可以带上 Retry-After,RFC 9110 将它定义为一个 HTTP 日期或需要等待的秒数。RFC 6585 并没有规定服务器如何识别客户端、如何计数请求:这部分是你自己的设计,所以要画在图上。IETF 有一份草案定义了标准的 RateLimit 和 RateLimit-Policy 头部字段,但截至撰写本文时(2026 年 10 月),它仍是草案。

决定计数存储宕机时怎么办。 失败时放行(fail open)能让 API 在没有限流的情况下继续运行;失败时拒绝(fail closed)则保护服务、拒绝流量。两者都可能是对的,只要这是一个有意的决定,而不是意外。

为前端服务的后端(BFF)

Sam Newman 的 Backends For Frontends 模式,用每种用户体验各自对应的服务端组件,取代一个通用的 API 后端。每个 BFF 只专注于一个界面,通常由构建该界面的团队维护,这样团队就能随着页面的变化调整 API。共享的通用后端往往会成为瓶颈,文章还指出,做聚合的边缘服务容易积累臃肿的代码。

需要几个 BFF?Newman 引用了 Stewart Gleadow 的一条简单规则:一种体验,一个 BFF。如果两个应用提供相同的体验、并由同一个团队负责,它们可以共用一个;例如 SoundCloud 的 iOS 和 Android 听众应用就共用一个 BFF。

合作伙伴的情况不同。他们需要稳定、带版本号的 API,而不是围绕某个页面设计的 API,所以他们的路由不经过 BFF,直接到达服务的公开 API。

示例:一家网店

这家网店有一个 React Web 应用、iOS 和 Android 应用,以及通过 API 创建订单的合作伙伴。WAF 在网关之前过滤流量。网关校验令牌,在 Redis 中按客户端计数请求,并按路径前缀路由。Web 团队和移动团队各自负责一个 BFF,两个 BFF 都会调用私有网络内的 Orders、Catalog 和 Accounts 服务。合作伙伴的调用直接到达 Orders 服务带版本号的 API。

Web 应用、移动应用和合作伙伴系统通过 HTTPS 先访问 WAF,再到达 API 网关。网关从身份提供商获取签名密钥,并在 Redis 中计数请求。它把 /web/* 路由到 Web BFF,把 /mobile/* 路由到 Mobile BFF,把 /partner/v1/* 路由到 Orders 服务。

14 个组件 · 14 个连接

在 Archboard 中打开
图表内容
  • Web app
  • Mobile app
  • Partner system
  • WAF
  • API gateway
  • Identity provider
  • Rate limit counters
  • Private network
  • Web BFF
  • Mobile BFF
  • Orders service
  • Catalog service
  • Accounts service
  • HTTPS
  • filtered
  • sign in (OIDC)
  • JWKS, cached
  • count per client
  • /web/*
  • /mobile/*
  • /partner/v1/*
面向 Web 应用、移动应用和合作伙伴的 API 网关:流量先经过 WAF,网关用身份提供商的密钥校验令牌,并在 Redis 中按客户端计数请求,然后路由到每个前端对应的后端,或直接路由到合作伙伴 API。

这张图的读法如下:

  • 从左到右,一个请求。 客户端经过 WAF 调用网关,网关再把请求路由到某个 BFF 或合作伙伴 API。
  • 虚线连线不在请求路径上。 Web 应用通过身份提供商登录,网关获取并缓存提供商的签名密钥。
  • 网关下方, Redis 保存所有网关实例共享的限流计数器。
  • 私有网络内, 每个 BFF 只调用其页面需要的服务。
  • Markdown 块是网关策略:它检查什么、限额是多少,以及哪些事留给服务。

需要标注什么

  • 路由: 每个后端的路径前缀或主机。
  • 每个入口的认证: 令牌类型、签发者,以及网关检查什么。
  • 限额: 计数键、次数和窗口,以及客户端会收到什么。
  • 网关上的超时与重试。 边缘的重试会成倍放大本已吃力的服务的负载。
  • 故障行为: 身份提供商、计数存储或某个后端不可用时会发生什么。
  • 归属。 网关配置通常由平台团队负责;BFF 由各个界面团队负责。

常见错误

  • 在网关里写业务逻辑。 改写和条件判断会逐渐长成没人能测试的规则。保持网关轻薄。
  • 把网关当作唯一的安全检查。 网关后面的服务不应该仅仅因为请求到达了就信任它。零信任架构图展示了如何在每一跳上画出检查。
  • 只按 IP 限流, 这会惩罚共用地址的用户,却放过不断更换地址的客户端。
  • 每个实例使用本地计数器, 这会让限额乘以实例数量。
  • 所有东西共用一个 BFF, 那又变回了一个通用后端。
  • 没有细节的 429。 不知道该等多久的客户端只能猜,而且通常猜得太短。

关于网关背后的部分,请参阅微服务架构图。在 Kubernetes 上,Kubernetes Ingress 图介绍了经常位于网关之前、或替代网关部分职责的那一层。

在 Archboard 中构建

  1. 使用 Google 或 GitHub 登录,并新建一张图。
  2. 添加 C4 模型节点包中的 容器,Web 应用和移动应用各一个,再添加一个 外部系统 表示合作伙伴。
  3. 添加安全节点包中的 WAF 和 身份提供商。
  4. 添加云节点包中的 API 网关,以及数据节点包中的一个 Redis 节点,用于限流计数器。
  5. 添加基础节点包中的一个 框架,命名为 Private network,并在其中放入 C4 模型节点包中的 容器 节点,表示两个 BFF 和三个服务。
  6. 从左到右连接它们,为每条路由标注路径,并把登录和获取密钥画成虚线。再添加一个基础节点包中的 Markdown 块,写明网关策略。

这个示例共用 14 个图表节点,在 Free 套餐的限制之内。

常见问题

API 网关和负载均衡器是一回事吗? 不是。负载均衡器把请求分发到各个实例。网关理解 API 请求:路由、令牌和配额。很多网关都运行在负载均衡器之后。

有了 API 网关,还需要服务网格吗? 它们解决的是不同的问题。网关处理来自外部的流量;网格处理服务之间的调用,例如双向 TLS 和重试。如果两者都在用,就分开画。

网关已经校验过令牌,服务还需要再校验吗? 需要,针对服务所依赖的那些声明。网关会尽早拒绝无效令牌,而每个服务仍要自己对操作进行授权。

应该有几个 BFF? 每种用户体验一个。提供相同体验、由同一团队负责的两个应用可以共用一个。

429 响应应该包含什么? 一段简短说明,以及一个写明需要等待秒数的 Retry-After 头部,这样客户端会先退避,而不是立即重试。

免费开始 — 1 个席位,无需信用卡。

相关文章

← 返回博客

一分钟打开画布

使用 Google 或 GitHub 登录。单席位免费,团队加入后再升级。