服务API版本控制设计与实践

标签: 服务 api 版本控制 | 发表时间:2021-11-25 19:12 | 作者:Andy_Lee
出处:http://weekly.dockone.io

【编者的话】笔者曾负责vivo应用商店服务器开发,有幸见证应用商店从百万日活到几千万日活的发展历程。应用商店客户端经历了大大小小上百个版本迭代后,服务端也在架构上完成了单体到服务集群、微服务升级。

下面主要聊一聊在业务快速发展过程中,产品不断迭代,服务端在兼容不同版本客户端的API遇到的问题的一些经验和心得。一方面让团队内童鞋对已有的一些设计思想有一个更彻底的理解,另一方面也是希望能引起一些遇到类似场景同行的共鸣,提供解决思路。

通用解决方案

应用商店客户端迭代非常频繁,发布新的APP版本的时候,势必导致出现多版本,这样服务端就会导致多个不同的客户端请求。强制用户升级APP,可能会导致用户流失,因此采用多版本共存就是必须的。以下是业界讨论过的的一些SOA服务API版本控制方法参考[1]。在实际开发中原则上离不开以下三个方案。
  • 方案一:The Knot 无版本——即平台的API永远只有一个版本,所有的用户都必须使用最新的API,任何API的修改都会影响到平台所有的用户。(如下图1)
  • 方案二:Point-to-Point——点对点,即平台的API版本自带版本号,用户根据自己的需求选择使用对应的API,需要使用新的API特性,用户必须自己升级。(如下图2)
  • 方案三:Compatible Versioning——兼容性版本控制,和The Knot一样,平台只有一个版本,但是最新版本需要兼容以前版本的API行为。(如下图3)



引用自: The Costs of Versioning an API

简单分析,The Knot只维护最新版本,对服务端而言维护有一定简化了,但是要求服务使用者及时适配最新的版本,这种做法不太适用用户产品,目前内部服务比较适用。Point-Point针对不同客户的版本提供独立的服务,当随着版本的增加开发和运维的维护成本会增加,这种在后面我们面对“协议升级”的时候有使用。

方案三应该是最常用的情况,服务端向后兼容。后面案例也主要采用这种思想,具体的做法也是有很多种,会结合具体的业务场景使用不同策略,这个会是接下来讨论的重点。

具体业务场景面临的挑战和探索

The Knot 无版本和Point-to-Point模式的应用场景


上图是我们应用商店迭代变化的一个缩影,业务发展到一定阶段面临以下挑战:
  • 业务发展前期,作为服务提供方,服务端不仅要支撑多个版本应用商店客户端,同时服务于软件侧的PC助手;
  • 产品形态变化多样,服务端接口变更和维护面临多版本客户端兼容的挑战;
  • 架构逻辑上,服务端采用早期传统架构,开发和维护成本比较高;服务端与客户端进行交互的协议优化升级;以及服务拆分势在必行。


所以服务端协议、框架升级以及公共服务拆分是首要解决的方向。改造经历了两个过程:
  • 阶段一:新版本新的接口一律采用新的JSON协议;已有功能接口进行兼容处理,根据客户端版本进行区分,返回不同协议的格式内容。
  • 阶段二:随着业务迭代,新的版本商店依赖的所有接口都完成了协议升级后,为了提升服务的稳定性,旧的协议性能无法明显提升,一方面升级后端架构和框架,提升开发效率和可维护性。同时拆分和独立新的工程,实现历史工程只提供给历史版本使用。我们针对大流量高并发、以及基础服务场景比如首页、详情、下载进行独立服务独立拆分。同时也提取一些公共的内部RPC服务,比如获取应用详情、过滤服务等。



经过改造,服务端架构如上图所示。
  1. 至此Old-Service后续只用进行相应的维护工作即可,对应Point-to-Point版本。
  2. 内部的RPC服务由于只提供内部服务,服务端和客户端可以随时同步升级,只要维护最新的版本就可以,采用The Knot模式。这里需要注意的是服务的升级需要注意保持向下兼容,在扩展字段或者修改字段的时候需要特别小心,不然可能在服务升级的时候会引起客户端调用的异常。


Compatible Versioning:兼容性版本控制

兼容性版本控制应该是最常见的版本控制方式,特别是在C/S架构当中,具体的兼容性版本不同的策略总结有API版本、客户端版本号、功能参数标志等。

场景一:API版本号控制

随着互联网发展的,用户体验要求也是越来越高,产品形式也会随之每年有不一样的变化。除了避免审美疲劳外,也是在不断探索如何提升屏效、点击率和转化。就拿应用商店首页列表举例。

应用列表在形态上经历过单一的 应用双排 -> 单排 -> 单排+穿插的布局。内容上也经历了不同商业化模式、人工排期到算法等演进。

每个版本接口内部逻辑变化是十分大的,有明显差异。如果只是简单在service层根据版本进行判断处理,会导致处理逻辑会变得异常复杂,并且还可能导致对低版本产生影响。同时商店首页是十分重要的业务场景,结合风险考虑,类似这样对场景,在接口URL上新增版本字段,不同对版本使用不同的值,在控制层根据不同的版本进行不同的处理逻辑会更加合理,简单有效。具体策略也有比如在URL上新增接口版本字段/{version}/index、请求头携带版本参数等。

场景二:客户端版本号控制

类似首页列表,商店的穿插Banner也经历了多个版本的迭代。如下图所示。这些穿插样式都是在不同版本下出现的,在样式布局,支持跳转能力等方面各个版本的支持程度不一样,接口返回时需要进行相应的处理适配、过滤等处理。

这类场景如果采用场景一的方案升级新的接口也能够解决,但是会存在大量重复代码,而且新增接口对于客户端接口改造、特别是一些接口路径会影响到大数据埋点统计,也是有比较高的沟通和维护成本在里面。

为了提升代码复用性。使用客户端版本号控制是首选考虑策略。但是需要注意,如果只是简单的在代码层面根据客户端版本号进行判断,会存在以下问题需要考虑:
  • 代码层面会存在各种判断,造成的代码可读性差,有没有更加优雅的方法;
  • 存在一个客观情况。那就是客户端的版本号是存在不确定性的。由于客户端采用 火车发布模式,多版本并行开发,导致版本号存在变动、版本跳跃不连续的情况时有发生,也给服务端开发带来了不少困扰。


如何思考解决这些问题呢?其实对于不同的产品形态涉及的一些资源或者产品模块本身出现在不同的迭代周期,可以认为他们具备了版本或者时间的属性。站在程序员视角,把某个资源支持对应的客户端版本作为这个资源对象的一个成员属性。每种资源具有这种属性后,也有相应的逻辑行为来对应成员方法---根据属性进行过滤。这样的设计赋予资源了属性和行为后,资源具备了统一的、灵活的过滤能力,而不再是简单的硬编码根据版本进行if-else判断。

有了方案后,实施起来就比较容易了。开发分配资源ID,并且设置对应支持客户端版本范围。过滤逻辑统一到资源对象。

代码层面可以将过滤逻辑统一封装到一个工具类(示例代码),在各个业务接口返回进行过滤。更加优雅的方案是建立统一的资源上层类,封装资源过滤方法,所有资源位的资源对象实现该上层类,统一在获取资源逻辑完成过滤能力。

场景三:新增功能标识参数

应用商店业务主要提供用户发现和下载新应用、更新手机已安装的应用。商店有增量更新可以减小更新包体积,因此也叫省流量更新,有效提升用户体验。前期我们使用开源的增量算法,但是发现该算法在部分机器合成拆分包会耗时很长,甚至引起crash。

于是项目组寻求更加高效拆分算法。类似在这些已有接口的进行功能增强的场景,除了提供新的API或者内部简单通过客户端版本判断进行扩展外,有没有更好的方案呢?因为除了这些方案已知的弊端外,需要从长远考虑,比如前面提到的算法,后续还会不会存在升级的可能,下载接口会不会有更多能力的增强。

结合上面思考,在原来接口基础上新增 标志参数字段,表示该请求发出的客户端支持的能力。为了后续扩展,字段类型为整数值,不只是简单的boolean,服务端通过 位运算完成判断逻辑。客户端也摆脱某个功能与版本的强一致性,不用去记录某个版本具有某种能力。

关于接口设计的更多思考

最后补充一些踩过的坑和反思。服务端在提供接口时,不能仅仅关注接口的实现,更多的时候需要关注接口的使用方,他们使用的场景、调用时机等等。否则开发在对接口问题排查、维护花费的时间会比实际开发的耗时要多上好几倍。
  • 场景化:具体到什么是场景化呢,拿商店客户端的帮助用户检测手机安装的应用版本是否最新的服务举例,检测时机是存在不同的场景的,比如用户启动、用户切换wlan环境、定时检测等。当需要进行精细化分析,哪些请求是有效的,哪些会引起集中请求时,这个时候如果请求上没有场景区分,那么分析将无从下手。所以在与客户端沟通接口设计时,请带上场景这个因素。接口设计上可参考如/app/{scene}/upgrade,定义好各个场景名称,在路径上带上具体的场景,这样对线上不同来源请求量级、问题分析都会有很大好处。
  • 鉴权和服务隔离:除了场景需要考虑外,接口调用在分配时做好记录和鉴权以及服务隔离。比如商店的部分接口服务不仅提供给客户端,同时也会提供给手机系统应用调用。目前vivo上亿的存量用户体量,这里要十分小心,系统应用的调用量控制不当,并发可比商店本身要大的多。首先前期与服务调用方评估沟通、做好设计,避免出问题。即使在出问题时,也要有机制能够快速发现问题、能够分析出问题的来源,降低问题带来的损失。


至此上面解决问题的思路,都与具体业务以及背景有一定关系。随着技术不断迭代和发展,在移动端APP页面动态性,目前业界也有了更多高效的技术方案,比如谷歌的Flutter、Weex等。这些技术能够实现灵活扩展,多端统一,性能也能够接近native。不仅减少了客户端发版频次,也减少了服务端兼容性处理成本。目前我们vivo也有团队在使用和实践。

技术不断更迭,没有最好的方案,只有最适合的方案。开发过程中不仅满足当前实现,更多的是考虑到后续扩展性和可维护性。 开发不能一味追求高端技术,技术最终服务于业务,坚持长期主义,效率至上

原文链接: https://mp.weixin.qq.com/s/BYPJtU85IOYnej9_5onGEA

相关 [服务 api 版本控制] 推荐:

服务API版本控制设计与实践

- - DockOne.io
【编者的话】笔者曾负责vivo应用商店服务器开发,有幸见证应用商店从百万日活到几千万日活的发展历程. 应用商店客户端经历了大大小小上百个版本迭代后,服务端也在架构上完成了单体到服务集群、微服务升级. 下面主要聊一聊在业务快速发展过程中,产品不断迭代,服务端在兼容不同版本客户端的API遇到的问题的一些经验和心得.

RESTful API版本控制策略

- - ITeye博客
做RESTful开放平台,一方面其API变动越少, 对API调用者越有利;另一方面,没有人可以预测未来,系统在发展的过程中,不可避免的需要添加新的资源,或者修改现有资源. 因此,改动升级必不可少,但是,作为平台开发者,你必须有觉悟:一旦你的API开放出去,有人开始用了,你就不能只管自己Happy了,你对平台的任何改动都需要考虑对当前用户的影响.

http api服务网关

- - 互联网 - ITeye博客
http服务网关系统是一个对http服务进行治理的系统,通过该系统可以对调用方进行身份认证、服务授权许可(许可调用方使用某个http服务)、服务鉴权(是否能调用http服务)、请求流控、失败恢复、调用度量数据统计分析、服务依赖视图等. 整个系统架构如下图,分为三大部分:api网关、网关控制台、度量数据采集分析(这个未在图中体现).

comet 服务器 icomet 提供 Android API

- - 开源中国社区最新新闻
支持百万连接和 comet/push 服务器 icomet 日前提供了可用于 Android 移动开发的 Java API - iCometClient4j, 用于实现手机上的消息推送功能. 结合 icomet 的 HTTP endless chunk 模式, 可提供节省电池的长连接服务.. iCometClient4j项目地址: https://github.com/DuoZhang/iCometClient4j/.

微服务-API网关实战小结

- - 掘金 后端
 1.为什么需要API网关.         在业务开发中,后台经常要暴露接口给不同的客户端如App端、网页端、小程序端、第三方厂商、设备端等使用,由于技术栈比较统一使用了 Spring boot web开发框架. 所以刚开始统一封装了如鉴权、限流、安全策略、日志记录等集成的工具包,开发中只需要引入该工具包即可实现上述的功能,请求端通过 Nginx后将请求到对应的微服务集群的节点上.

怎么用API网关构建微服务|架构

- - 企业架构 - ITeye博客
转于http://www.tuicool.com/articles/bMnEbmv. 当选择将应用程序构建为一组微服务时,需要确定应用程序客户端如何与微服务交互. 在单体应用程序中,只有一组(通常是重复的、负载均衡的)端点. 然而,在微服务架构中,每个微服务都会暴露一组通常是细粒度的端点. 在本文中,我们将讨论一下这对客户端与应用程序之间的通信有什么影响,并提出一种使用API网关的方法.

Api Blocking

- - xiaobaoqiu Blog
4.RateLimiter实现限流. 接口限流是保证系统稳定性的三大法宝之一(缓存, 限流, 降级).. 本文使用三种方式实现Api限流, 并提供了一个用Spring实现的Api限流的简单Demo, Demo的git地址: https://github.com/xiaobaoqiu/api-blocking.

Google推出企业版Google App Engine、云存储服务和Prediction API

- Johnny - 36氪
Google云服务可以把用户的应用程序托管到云端,使用和Google一样的网络基础设施. 目前有20多万开发者在使用Google的云服务. Google今天又宣布了几款企业级云服务相关的产品,其中包括Prediction API、Cloud SQL、云存储等. 之前的Google Storage for Developers现在更名为Google Cloud Storage,价格比原来更低.

搭建微服务器:express+https+api代理 - 馒头加梨子 - 博客园

- -
service worker,但是service worker只能在https下跑,所以查资料自己用纯express搭建了一个微服务器,把过程记录下来,供以后开发时参考,相信对其他人也有用. 首先我们用express搭建一个. http服务器,很简单,看看官方文档就可以搭建出来了. 理想状况是,在项目目录下建立一个server文件夹,然后在server文件夹里面启动服务器,加载项目目录下的dist文件夹.

微服务和API网关限流熔断实现关键逻辑思路

- - DockOne.io
本文谈下微服务架构和API网关中的限流熔断,当前可以看到对于Spring Cloud框架本身也提供了Hystrix,主流的开源API网关产品类似Kong网关本身也包括了限流熔断能力. 当然也有完全较为独立的限流熔断开源实现,比如阿里的Sentinel即是我们经常会用到的限流熔断开源产品,而且可以和Dubbo,SpringCloud等各种微服务框架无缝集成.