首页 / 业界 / 正文

微服务接口版本兼容策略 踩坑后的落地经验

上周帮朋友排查线上故障,刚进群就看到运营在刷消息,说仓储系统漏了三百多单的发货通知,所有查订单的请求返回都是空。

翻了半小时日志才找到原因。

订单组前一天改了订单查询接口,把原来的返回字段goods_id改成了item_id,想着只是改个名字,反正前端用的是新字段,没当回事。结果仓储组的同步服务还在盯着goods_id取值,拿到null直接跳过了这条订单。

大促前一周出这种事,整个组连续加了三天班补数据,负责人的绩效直接扣了一半。

你说这是多大的技术难题?

根本不是。

就是没把接口版本兼容当回事。

别拿“小改动”不当破坏性变更

别拿“小改动”不当破坏性变更

很多人对兼容的理解有偏差,觉得只要不删字段就是兼容。大错特错。

只要修改了已有字段的类型、改了字段的命名、调整了必填字段的返回规则、或者修改了枚举值的范围,哪怕只是少返回了一个原本调用方依赖的非必填字段,都是实打实的破坏性变更。

我之前碰到过更离谱的,有个开发把返回的价格字段从string类型改成了number类型,觉得只是类型更规范了,结果老版本的APP拿到数字直接做了字符串拼接,用户看到的价格变成了“¥123.456”这种鬼样子,应用商店的差评刷了两百多条。

别侥幸。

调用方的逻辑永远比你想的“野”。你以为没人用的字段,可能藏在某个三年前写的老服务里,连负责人都换了三茬,没人记得它还在跑。对吧?你永远猜不到哪个犄角旮旯的脚本还在定时调你的接口。

版本号别搞花里胡哨的设计

现在网上很多教程说版本号要放在请求头里,要放在请求参数里,要用语义化版本号,搞一堆规则。

说实话,大部分中小团队根本用不上那么复杂的东西。

最靠谱的方案就是直接把版本号放在URL路径里,比如/api/v1/order/query,/api/v2/order/query。

新人接手不用翻半天文档找“请求头要带什么参数”,网关层的路由规则也一目了然,出了问题看请求路径就知道是哪个版本的接口在报错。

不过话说回来,也不是所有团队都适合这套。如果是跨几十个部门的大型微服务集群,网关的路径配置已经到了几百条的规模,那再往路径里塞版本号确实会乱,这时候再考虑把版本号放在请求头里,用统一的网关插件做流量转发就行。

微服务接口版本号位置对比示意图
微服务接口版本号位置对比示意图

对了,别搞那种语义化版本号塞在路径里的操作,比如/api/order/query?version=2.1.3。你要是真的只是改了个小字段,犯得上升小版本号吗?最后路径里的版本号堆得乱七八糟,没人记得哪个版本对应哪个逻辑。

版本号的粒度要和变更的粒度匹配。只要是破坏性变更,直接升大版本号,比如从v1到v2,小的新增字段根本不用动版本号。

落地规则别搞一刀切

很多团队一上来就定死“所有接口必须按版本号严格隔离”,结果内部调用的接口也要搞两个版本并行,开发维护成本直接翻了一倍。

不对的。

接口的兼容策略要分场景定。

如果是同一个大部门内部的服务调用,比如订单和仓储都是交易中心的,大家抬头不见低头见,那就不用搞多版本并行的那套。约定好规则就行:新增字段随便加,要修改或者删字段,提前两周在群里发通知,所有依赖这个接口的服务负责人确认完了再改,改的时候先切10%的灰度流量到新版本,观察一天没问题再全量。

要是对外的开放接口,比如给第三方商家、合作方调用的接口,那得按严格的版本隔离来。旧版本的接口至少留6个月的支持期,下线前提前三个月发公告,给所有调用方留够迁移的时间。

我见过太多团队,对外接口刚上线三个月就说旧版要下线,合作方的技术支持电话被打爆,最后还要自己派开发帮着对方改代码,平白多了好多工作量。

微服务接口灰度发布流量切分示意图
微服务接口灰度发布流量切分示意图

还有个小技巧,每次改接口的时候,把所有调用这个接口的服务列表导出来,挨个@对应的负责人确认,别嫌麻烦。很多时候故障就是因为你觉得“这个服务应该没人用了”,结果人家还在跑,你改了字段直接给人干挂了。

哦对,还有个兜底的方案,在网关层做个字段映射。比如你要把goods_id改成item_id,那在网关层把旧请求的item_id自动映射到goods_id返回给老调用方,不用改老服务的代码,过渡完了再把映射规则删掉就行。

别觉得这是偷奸耍滑。线上稳定永远比代码优雅重要。

接口版本兼容从来不是什么高深的架构问题。

就是个责任心的事。

别总想着“我改完就完事了”,多想想调用方的场景,少出几次线上故障,比你学十篇架构教程都有用。