项目概述
本项目实现一个可配置的 API 网关:按方法与前缀把请求路由到上游服务,在边缘完成一次鉴权,按路由限流并设置转发截止时间,最后把上游失败统一成带请求 id 的错误信封。目标是让网关成为一个可靠的策略收口点,而不是只做字节转发的代理。
架构与关键决策
路由表以纯数据描述方法、前缀、上游、限流与超时,在启动时校验,因此新增路由是一次配置变更而非代码改动。鉴权在任何上游被触达之前完成,避免未认证请求占用后端资源;令牌桶按路由与主体分别计数,防止单一噪声客户端耗尽他人配额;转发使用 AbortController 绑定截止时间,慢上游以 504 结束而非长期占用连接。
实施与测试
通过可延迟、可失败的上游桩驱动真实 HTTP 请求,断言路由命中、未认证被拒、超限返回 429、超时返回 504,以及错误信封包含请求 id。由于时钟与上游桩都在测试控制之下,这些用例是确定性的。
限制与后续
- 请求与响应体直接透传,不做过滤或重写。
- 路由表启动时加载,不支持热更新。
- 未实现 WebSocket 与 HTTP/2 代理。
代码示例
pnpm init
pnpm add express undici jose
pnpm add -D typescript tsx vitest
api-gateway/
├── src/routes.ts
├── src/proxy.ts
├── src/auth.ts
├── src/limits.ts
├── src/errors.ts
├── tests/gateway.test.ts
└── package.json
const routes = [
{ method: 'GET', prefix: '/users', upstream: 'http://users:8080', limit: 20 },
{ method: 'POST', prefix: '/orders', upstream: 'http://orders:8080', auth: true },
];
const { payload } = await jwtVerify(token, jwks, { issuer, audience });
req.subject = payload.sub;
if (!bucket.take(subject, route.limit)) return res.status(429).json({ error: 'rate_limited' });
const ac = new AbortController();
const t = setTimeout(() => ac.abort(), route.timeoutMs);
const upstream = await fetch(route.upstream + req.url, { signal: ac.signal });
res.status(code).json({ error, requestId: req.id });
pnpm vitest run