服务化体系之-兼容性与版本号

1,782 阅读4分钟
原文链接: mp.weixin.qq.com

准备挽起袖子写个服务化的大系列,不想错过的同学,先把公众号关注了,jnby1978。

家大业大之后,服务的版本和兼容性就是个让人不得不正视的问题。

最近,路上有个说法,既然都是微服务了,那不同的版本可以认为是两个完全不一样的微服务,没必要再保留版本号了。

这篇文章按着唯品会的实战经历来探讨一下。

我们面对的现状是:服务每周每天都在升级,有些升级是业务逻辑升级,接口不变;有些是接口变了,但是兼容的,偶然还有一些是不兼容的。

家大业大的体系永远不可能在服务端接口升级的时候,同时(比如同一个深夜,同一个小时甚至同一分钟)将所有调用者的客户端也升级的,相反所有客户端的升级可能是很长一段时间的事情,而且可能客户端还没全升完,服务端的接口又变了。

1. 兼容性原则

先搞些铺垫,我们一般认为下面的情况是兼容的:

  1. 增加新方法

  2. 增加可选的参数

  3. 修改参数为可选

  4. 删除参数

  5. 框架支持的话,参数的名称、顺序也可以改(如Thrift)

参数是对象的话,其属性的变更参见2-5条。

然后一般认为下面的情况是不兼容的:

  1. 业务上的不兼容

  2. 修改方法的名称

  3. 删除方法

  4. 增加必填的参数

  5. 修改参数为必填

  6. 修改参数和返回值的类型

参数是对象的话,属性的变更参照3-5条。

2. 不兼容时服务如何升级?

服务要进行不兼容的升级时,前面说了客户端不可能同时升级的,那怎么办?

一种是不修改原来的接口,直接在同一个服务里增加新的方法来解决。这种应该是最简单的做法,不过日积月累下会有点dirty。

一种是同时运行新旧两个服务。这种方式干净,但麻烦,要注意:

  • 要保证旧版的客户端SDK,调用不能被路由到新服务上。

  • 随着客户端不断割接过来,控制好新旧两个的集群容量。

3. 版本号原则

服务的版本号,和软件的版本号一样,一般整成三位:

第一位:不兼容的大版本,   如1.0    vs  2.0

第二位:兼容的新功能版本,如1.1    vs 1.2

第三位:兼容的BugFix版本,如1.1.0 vs 1.1.1

如果拿着低版本的SDK(如1.0.0) 发起请求,会被服务化框架路由到所有的兼容版本上(如1.1.1,1.2.0),但不会到不兼容的版本上(如2.0.1)。

4. 最终问题,到底要不要版本号?

4.1 版本号用于标示SDK版本是有益的

即使是兼容的小版本。

版本号能让服务端与客户端两头的开发人员更好的对话。毕竟所谓兼容,有时候也有着某种代价与折衷,互相明确彼此的版本会更好。

如果服务治理中心,能让所有服务提供者一目了然各个调用者的版本会更好,起码方便催人升级呀。

同时,在中央服务文档中心,有了版本号后也能为服务接口每一个小版本保留一份文档。

4.2 不兼容的版本,是独立两个服务,还是用第一位版本来区分?

像我们这种命名渣,好不容易为服务搞了个贴切的名字,再想第二个其实很不容易,一般只能在函数名,服务名里直接带上数字,如GoodsService2,或者GoodsServiceNew, GoodsNewService.....

既然这样,还不如继续使用版本号。当然,前提是你家的服务化框架有这个版本路由能力,刚好我们家有。当然,引入了版本,在框架里也引入了一定的复杂度,见后。

4.3 简化基于版本的配置

我们家框架之前的设计,将配置的粒度支持到每个小版本上,比如1.0版本的超时是10ms,1.1版本是20ms。然后根据客户端的版本来就近选择。回头来看,白白增加复杂度....

其实兼容的小版本只有一份配置就好,明年得改掉。

但不兼容的大版本还是要两份,比如自定义的路由。不兼容的版本本质上真的就是两个微服务了。

4.4 最终结论

建议还是有版本, 服务化框架里的相关逻辑,只取第一位的主版本号。第二、三位则用于SDK版本的管理沟通,以及中央服务文档中心。

当然,以上只是针对唯品会这种体量的公司的一家之言,各有各的玩法,并非四海皆准。

查看图片