staging环境跑得很漂亮。接口干净、响应飞快、错误处理优雅——那种能在 sprint review 里昂首挺胸演示的 API。然后市场部搞了波 campaign,流量 spike,整个系统以一种没人预料到的方式崩了。不是因为服务器不够,也不是数据库慢。是因为 API 设计时就假设了「只有原始开发者才会按预期方式调用它」——流量可预测、客户端守规矩、数据集永远很小。扩容不是纯基础设施问题,是设计问题,而流量只是暴露它的那个东西。
下面这 5 个决策,区分了「能扩容的 API」和「勉强苟活的 API」。
1. 第一天就版本化,别等到第一百天
API 版本控制是可扩容设计的安全带——人人都跳过,直到 desperately need it 的那一刻。错误不是恶意的,是乐观的。团队假设 schema 不会变、客户端会包容、「等需要了再加版本」。我学到的是:later 永远以 breaking change 的形式到来,对面站着真实用户。
v1 的契约一旦发布,就不是你的了。是用户的。他们基于此写了代码、建了集成、做了业务决策。你改字段名、删可选参数、调整响应结构,他们的代码就炸。没有版本号,你只有两种选择:永远冻结 v1(技术债务滚雪球),或者强行推送变更(用户骂街)。
版本化从 day one 开始,成本几乎为零。URL 里塞个 /v1/,或者 header 带 X-API-Version,都行。关键是让用户知道:这个契约是稳定的,你可以放心依赖。等 v2 来了,v1 继续跑,给用户迁移窗口。这才是专业做法。
2. 分页不是功能,是生存机制
开发阶段,GET /users 返回 200 条记录,毫秒级响应,很爽。生产环境跑了两年,用户表涨到 200 万条,同一个接口超时 30 秒,数据库 CPU 飙到 100%。这时候再加 ?page=2&limit=50?太晚了。客户端已经习惯了「一次拿全部」,你改成分页就是 breaking change。
分页必须从第一版就存在,哪怕当时只有 50 条数据。cursor-based 比 offset-based 更适合高并发场景——深分页时 offset 100000 会让数据库做大量无用功,而 cursor 直接定位。返回结构里带上 hasMore 和 nextCursor,客户端自然知道怎么继续。
更隐蔽的坑是「默认无限制」。不写 limit 就返回全部,这是给未来的自己埋雷。设置一个合理的上限(比如 100),超过就强制分页,保护数据库也保护响应时间。
3. 幂等性:让重试变得安全
网络抖动时,客户端发了 POST /payments,超时了。它不知道服务器到底收到没,于是重试。如果接口不是幂等的,用户就会被扣两次款。这是支付场景的经典噩梦,但同样适用于任何写操作:创建订单、扣减库存、发送通知。
幂等性设计很简单:客户端生成唯一 idempotency key(幂等键),随请求一起发。服务器用这个 key 做去重,相同 key 的重复请求直接返回上次结果。Stripe 的 API 把这做成行业标杆——他们的 idempotency-key header 让重试变得无脑安全。
关键是在 day one 就预留这个机制。事后加幂等性,要改数据库 schema、要处理历史数据冲突、要协调客户端升级。成本从「加个 header」变成「重构核心交易链路」。
4. 限流:比用户先知道自己在崩溃
没有限流的 API,就像没有刹车的车——速度起来之后,停下来只能靠撞墙。429 Too Many Requests 不是惩罚用户,是保护系统。好的限流设计分两层:基于身份的(API key 级别)和基于资源的(IP 或全局)。
返回头里带上 X-RateLimit-Limit、X-RateLimit-Remaining、X-RateLimit-Reset,让用户知道边界在哪。别等 500 了才告诉人家「你调用太猛了」,那时候已经晚了。限流策略要可配置,campaign 期间临时调高阈值,日常收紧,灵活应对。
一个细节:限流错误信息要具体。「Rate limit exceeded」不如「You have exceeded 100 requests per minute. Retry after 2024-01-15T09:23:00Z」。用户能据此调整逻辑,而不是盲目重试。
5. 可观测性:设计时就埋好探针
上线后排查问题,最绝望的不是「不知道哪错了」,是「根本看不到」。每个端点要有 latency histogram、error rate、throughput 三件套。不是事后加监控,是设计时就定义:这个接口的 SLO(服务等级目标)是什么?p99 延迟 200ms?错误率低于 0.1%?
分布式追踪(distributed tracing)让跨服务的问题有迹可循。一个请求经过网关、认证服务、业务服务、数据库,trace ID 串起整条链路。没有它,你只能在各服务的日志里 grep,像拼图一样猜全貌。
日志结构要统一,用 JSON 而不是自由文本。字段命名一致:timestamp、level、trace_id、user_id、endpoint、status_code。这样日志平台能直接聚合分析,而不是写一堆正则表达式去解析。
这 5 件事,staging 环境都测不出来。它们只在流量 spike、客户端异常、数据膨胀时显现价值。而那时候,你已经没有从容重构的窗口了。
你的 API 现在处于哪个阶段?day one 的版本号埋好了,还是已经在 day one hundred 的 breaking change 边缘试探?
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.