4. API 版本管理:URL路径版本、请求头版本、参数版本策略
API 版本管理这事,说白了就是给你的接口上「保质期」。我见过不少团队,一开始图省事没做版本管理,结果线上服务升级时,老客户端全挂了——那场面,真是灾难。
特征平台的 Serving API 尤其敏感。你想想看,线上推理服务依赖的特征取值逻辑,一旦变了,模型预测结果可能天差地别。所以版本管理不是「要不要做」的问题,而是「怎么做才不坑」的问题。
核心原则:版本一旦发布,就是不可变的。老版本必须继续服务,直到所有客户端都迁移走。
4.1 三种主流策略,我该怎么选?
目前业界主流的版本策略就三种:URL 路径版本、请求头版本、参数版本。每种都有它的适用场景,我一个个说。
| 策略 | 典型示例 | 优点 | 缺点 |
|---|---|---|---|
| URL 路径版本 | /v1/features/batch |
直观、易调试、缓存友好 | URL 膨胀、版本迁移成本高 |
| 请求头版本 | Accept: application/vnd.feast.v2+json |
URL 干净、灵活、适合微服务 | 调试麻烦、网关配置复杂 |
| 参数版本 | GET /features?version=2 |
实现简单、兼容性好 | 参数污染、容易被忽略 |
4.2 URL 路径版本:最直接,也最「笨」
我个人习惯在特征平台初期先用 URL 路径版本。为什么?因为直观。你打开浏览器就能看到版本号,排查问题的时候少了很多猜疑。
# 特征平台 Serving API 路径版本示例
GET /v1/features/user:123
GET /v2/features/user:123
POST /v1/features/batch
POST /v2/features/batch
我在项目中遇到过一件事:有个团队把版本号放在路径里,但升级时直接把 /v1 的代码删了。结果线上还有一批 IoT 设备在用老版本,全部 404。嗯,这里要注意——路径版本必须保证老版本的代码和部署是独立保留的,不能简单覆盖。
避坑指南:我曾经见过一个项目,URL 路径版本从 /v1 一路升到 /v12,每个版本都复制一份代码。这其实不对。路径版本应该只保留最近 2-3 个活跃版本,更老的版本要主动推动客户端升级,而不是无限保留。
4.3 请求头版本:优雅,但需要好网关
请求头版本,说白了就是通过 HTTP 头部来协商版本。这种方式在 RESTful 设计里更「正统」,因为 URL 只代表资源,版本是协议的一部分。
# 请求头版本示例
# 客户端请求
GET /features/user:123
Accept: application/vnd.feature-platform.v2+json
# 服务端响应
HTTP/1.1 200 OK
Content-Type: application/vnd.feature-platform.v2+json
这种方式有个好处——URL 永远不变。你想想看,客户端只需要改一个请求头,就能切换到新版本。但坏处也很明显:调试的时候,你得翻请求头才能知道用的是哪个版本,curl 命令也得多写一行 -H。
我建议在以下场景用请求头版本:
- 你的 API 网关支持自定义头部路由(比如 Kong、APISIX)
- 客户端是内部服务,有统一的 HTTP 客户端封装
- 你希望 URL 保持干净,方便 CDN 缓存
小技巧:如果使用请求头版本,建议在响应头里也返回当前版本号。这样客户端可以确认自己调对了版本,排查问题时也少了很多扯皮。
4.4 参数版本:简单,但容易「漏」
参数版本是最容易实现的——在查询参数或请求体里加一个 version 字段。但说实话,我不太推荐把它作为主要版本策略。
# 参数版本示例(不推荐作为主策略)
GET /features/user:123?version=2
POST /features/batch
{
"version": 2,
"requests": [...]
}
为什么我不推荐?因为参数太容易被忽略了。我遇到过的情况是:客户端代码里忘了传 version 参数,服务端默认用了 v1,结果新功能没生效,排查了半天才发现是版本号没传。
参数版本更适合作为版本内的子版本,比如:
- URL 路径是
/v2/features,参数?sub_version=experimental用于 A/B 测试 - 或者作为降级策略:当请求头版本协商失败时,回退到参数版本
4.5 特征平台的版本管理实战
在特征平台里,版本管理不只是 API 层的版本,还涉及特征定义、特征视图、特征服务的版本。我一般这样设计:
# 特征平台版本管理分层
# 1. API 版本:/v1/features, /v2/features
# 2. 特征定义版本:feature_view:v1, feature_view:v2
# 3. 特征服务版本:serving_job:v1, serving_job:v2
# 实际调用链路
POST /v2/features/batch
{
"feature_service": "user_profile:v2",
"entities": [{"user_id": 123}]
}
这里有个关键点:API 版本和特征版本是解耦的。v2 的 API 可以调用 v1 的特征服务,反过来也行。这样你在升级 API 时,不需要同时升级所有特征定义,降低了风险。
我的建议:特征平台的 Serving API 优先使用 URL 路径版本。原因很简单——特征平台的调用方通常是数据工程师或算法工程师,他们需要快速定位问题。URL 里直接看到版本号,比翻请求头要快得多。
4.6 版本生命周期管理
版本不是「发布即永存」的。你需要一个清晰的版本生命周期:
- 开发版:内部测试,版本号带
-alpha或-beta后缀 - 稳定版:正式发布,全量可用
- 废弃版:标记为废弃,但仍在服务,响应头加
Warning: 299 - "Deprecated" - 下架版:停止服务,返回 410 Gone
我一般给每个版本留 6 个月的废弃期。这期间会在响应头里加 Deprecation: true 和 Sunset: Sat, 31 Dec 2024 23:59:59 GMT,让客户端知道什么时候必须迁移。
# 版本废弃响应示例
HTTP/1.1 200 OK
Deprecation: true
Sunset: Sat, 31 Dec 2024 23:59:59 GMT
Content-Type: application/json
{
"features": {...},
"_warning": "This API version will be removed on 2024-12-31"
}
血的教训:我曾经直接下架了一个 v1 版本,没给废弃期。结果有个离线批处理任务用的是 v1,每天凌晨跑,第二天才发现数据全挂了。从那以后,我所有版本下架前都会先走「废弃期 + 监控告警」流程,确保没有流量了再动手。
4.7 一张图看懂版本策略选择
下面这张图总结了三种版本策略的适用场景和选择逻辑:
4.8 总结一下我的建议
说了这么多,其实就三句话:
- 特征平台对外暴露的 API,用 URL 路径版本。简单、直观、好排查。
- 内部服务间调用,用请求头版本。URL 干净,网关好做路由。
- 参数版本,只用来做实验性功能或降级兜底,别当主策略。
版本管理这事,选什么策略不是最关键的。最关键的是——定下来就别轻易改。我见过最折腾的项目,半年换了三次版本策略,客户端团队跟着改了三轮,最后大家都不想维护了。你想想看,这得多伤士气。