TT Lab
开始
学习 学习路径 课程

构建 EAI 中间层

门卫不做屋里的活

在 TT Lab 中继续学习

一句话总结

API 网关站在 HTTP API 的前面,在一个地方决定谁(认证)、多少(配额)、用哪个版本(路由)来调用。如果说 EAI 枢纽是翻译报文、连接系统之间,那么网关则是在不了解业务逻辑的情况下,只扮演调用的守门人。无论是使用产品(Kong、Apigee 等)还是用 nginx 来搭建,所做之事的原理是相同的。

为什么需要它

假设银行向合作方和金融科技公司开放了查询 API。起初由 API 服务器自己核对密钥、统计调用次数、把旧版本的请求转给新版本。当 API 服务器增加到三台,三处的密钥核对代码就慢慢变得各不相同。某个合作方因为 bug 每秒涌入数千次请求,所有合作方都随之变慢。想要下线 v1,却没有人知道还有谁在用 v1。

这三件事——认证、调用量限制、版本——与业务无关,对所有 API 都同样需要。所以不是在每台 API 服务器上各自实现,而是集中到前端一处。反过来,如果开始往网关里放业务规则(余额确认、限额计算),网关就成了第二个应用服务器。守门人只看身份开门,不做屋里的活。

工作原理

本实验不使用产品,而是用 nginx 的基本模块来构造原理。只使用官方文档中的指令。

认证——把 API 密钥换成消费者名称。map 是把请求值(这里是 X-API-Key 头,变量 $http_x_api_key)转换成另一个变量的表。把密钥 → 消费者名称的表放在文件里,用 include 引入。表中没有的密钥取默认值(空值),空的消费者以 401 打回。对上游只把消费者名称以 X-Consumer 传递,而不传递密钥本身——如果把 proxy_set_header 的值设为空字符串,这个头就不会传给上游。机密在网关处止步,密钥也不会泄漏到上游日志中。(API 密钥只是识别发起调用的程序,并不认证用户。如果需要用户级别的权限,就要使用令牌——这不在本课程范围内。)

配额——按消费者的速率限制(rate limit)。正如文档所述,limit_req 模块以“漏桶(leaky bucket)”的方式限制请求处理速率。用 limit_req_zone $consumer zone=… rate=5r/s 以消费者名称为键创建区域,限制就会对每个消费者分别生效(如果以 IP 为键,同一个 NAT 背后的所有合作方就要共用一个桶)。按文档所述,键为空的请求不计数。burst 决定把瞬间涌来的请求排队多少个,nodelay 决定对排队的请求是否不延迟、立刻处理。超过限额的请求默认收到 503,而用 limit_req_status 429 改掉——429 Too Many Requests 是 RFC 6585 定义的状态码,表示“发送的请求太多”,这样调用方就能区分“服务器出问题了(503)”和“是我发得太多了(429)”。

版本——路径与请求头。把版本放在路径中的方式(/v1/…、/v2/…)看得见,在缓存和日志中也容易区分。通过请求头选择的方式(X-API-Version: 2)则不改变路径。本实验两种都接受:没有版本的 /api/… 通过请求头选择,没有请求头则为 v1。而对即将下线的版本,要加上 Sunset 头(RFC 8594),告知“这个资源在这个时刻之后可能不再响应”。值是 HTTP 日期格式。它附在每个响应上,所以调用方的日志和监控会自己发觉——比通知邮件更可靠。

请求 ID。如果调用方发送了 X-Request-ID,就原样传递,没有则由网关生成。按文档所述,nginx 的 $request_id 是把随机 16 字节写成十六进制的唯一标识符。如果把这个 ID 与消费者、状态一起记录在访问日志里,当合作方说“昨天 14 点收到了 429”时,就能立刻找到那一行(这是把第 7 模块 GUID 的思路应用到 HTTP 边界上)。

超时。上游一停,网关的连接也会跟着停住。proxy_read_timeout 是从上游两次读取之间等待的最长时间,超过它网关就返回 504。默认值是 60 秒,如果是调用方 10 秒就放弃的 API,网关就没有理由抓着 60 秒不放(第 4 模块的“越往里越短”)。

与 EAI 枢纽有什么不同。枢纽会翻译报文(格式、代码),转换同步与异步,并组合多个系统。网关则是几乎原样放行 HTTP 请求,只做管控。在现场,两者是同时存在的——外部合作方 → 网关 → 枢纽 → 账户系统。

在现场相遇的样子

第一,用 IP 做配额的失误。大型合作方在 NAT 后面运行多个服务,互相挤占对方的限额。要先识别消费者。第二,把超出限额以 503 返回的配置。调用方的重试逻辑会把它看作“服务器故障”,而更猛烈地重试。第三,API 密钥留在上游日志中——合作方的密钥会以明文堆积在日志收集系统里。第四,版本下线通知只用邮件。已经换了负责人的合作方并不知情,到了下线那天就会遇到故障。

下一项实验要做什么

启动上游夹具(v1、v2,能把收到的请求头映射出来的 API),并逐步扩展 nginx 配置 nginx.conf——路径代理、API 密钥认证与传递消费者名称、按消费者的配额与 429、基于请求头的版本路由与 Sunset、请求 ID 与访问日志、上游超时。评分器会复制你的配置文件,用它自己的端口重新启动,并发送请求来测试。