今天我们来聊一聊在微服务项目中如何设计版本号。平时看到的项目版本号,基本上分为X.Y.Z三部分。版本升级的时候版本号是会变的,所以版本号怎么变不是靠头脑风暴决定的。今天我们就一起来探讨一下。看看这个话题。1.SemanticVersioningControl规范的版本号如何控制?其实有一个标准的规范,规范地址:https://semver.org/lang/zh-CN/这个规范很友好,提供了中文版的内容。语义版本控制规范要求版本号由三部分组成:MAJOR(X):这是主版本号,一般在涉及到不兼容的API变化时发生变化。MINOR(Y):这是次要版本号。当我们增强API以实现向后兼容性时,此版本号将会更改。换句话说,它会在有新功能时发生变化。PATCH(Z):这是修订号,当我们修复一些错误并发布版本时,它会改变。语义版本控制规范主要提出如下要求:使用语义版本控制的软件必须(MUST)定义一个公共API。API可以在代码中定义或出现在严格的文档中。不管是什么形式,都应该是准确和完整的。标准版本号必须采用X.Y.Z格式,其中X、Y和Z是非负整数,数字前面的零填充不得。X是主版本号,Y是次版本号,Z是修订号。每个元素必须按值递增。例如:1.9.1->1.10.0->1.11.0。标有版本号的软件发布后,不得更改该版本软件的内容。任何修改都必须在新版本中发布。有朋友可能会说,我们的项目正处于快速开发阶段,API不稳定,每天都在变,按这个要求多少个版本就够了!事实上,API的快速变化一般有两种主要情况。一种是项目刚建立时,此时主版本号为0,那么此时的API不能算是稳定的API;版本正在快速开发中,但是这种情况下一般都会有一个新的分支来管理下一个版本的代码,所以其实和这里的需求没有冲突(详见第4条和第5条)。主版本号为零(0.y.z)的软件处于开发的初始阶段,一切都可能随时更改。这样的公共API不应被认为是稳定的。版本号1.0.0用于定义公共API的构成。此版本后的所有版本号更新均基于公共API及其修改。那么有的朋友可能会疑惑版本号是什么时候从0.Y.Z变成1.Y.Z的?一般来说,当你的项目已经进入生产环境或者有稳定的API供他人使用时,基本可以算是1.Y.Z。当仅进行向后兼容性修复时,修订号Z(x.y.Z|x>0)必须递增。这里的更正是指对不正确的结果进行的内部修改。当新的向后兼容功能出现时,次要版本号Y(x.Y.z|x>0)必须递增。当任何公共API功能被标记为已弃用时,也必须递增。当向内部程序添加大量新功能或改进时,它也可能会增加,其中可能包括修订级别更改。每次次要版本号增加时,修订号必须清零。每当将任何不兼容的更改添加到公共API时,主版本号X(X.y.z|X>0)都必须递增。它可能包括次要版本号和修订级别更改。每当主版本号增加时,次版本号和修订号必须清零。前面的版本号可以在修订后标记,前面是一个连字符,后面是一系列用句点分隔的标识符。标识符必须由ASCII字母数字后跟连字符[0-9A-Za-z-]组成,并且不得留空。数字标识符不得在前面填零。预发布版本的优先级低于关联的标准版本。版本号上标有以前的版本表示该版本不稳定,可能不满足预期的兼容性要求。示例:1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。版本构建信息可以在修订版或以前的版本号之后标记,前面是加号,后跟一系列以句点分隔的标识符。标识符必须由ASCII字母数字后跟连字符[0-9A-Za-z-]组成,并且不得留空。在判断版本的优先级时,可以(应该)忽略版本编译信息。因此,当两个版本仅版本编译信息不同时,属于同一优先级。示例:1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85。版本的优先级是指不同版本在排序时如何比较。在判断优先级时,需要(MUST)将版本拆分为主版本号、次版本号、修订号、前版本号,然后进行比较(版本编译信息不包含在该比较列表中)。每个标识符按从左到右的顺序进行比较,第一个差值用于确定优先级:主版本号、次版本号和修订号进行数值比较。例如:1.0.0<2.0.0<2.1.0<2.1.1。当主版本号、次版本号和修订号都相同时,以优先级低的前一个版本号来确定。例如:1.0.0-alpha<1.0.0。具有相同主版本号、次版本号和修订版号的两个先前版本号的优先级必须通过每个点分隔的标识符从左到右进行比较,直到发现差异为止决策:仅数字标识符在数字上进行比较。当有字母或连字符时,它们按ASCII顺序逐字比较。数字标识符的优先级低于非数字标识符。如果开头的标识符都相同,版本号字段越多的优先级越高。例如:1.0.0-alpha<1.0.0-alpha.1<1.0.0-alpha.beta<1.0.0-beta<1.0.0-beta.2<1.0.0-beta.11<1.0.0-rc.1<1.0.0。2、微服务中的版本号那么微服务中的版本号应该如何设计呢?首先,总体思路是遵循上面提到的语义版本控制规范。其次,虽然上面给出了很多规章制度,但是在我们实际的开发中,一般只需要简单的考虑以下几个方面。每次我们发布时都通过这个规范显然是不现实的:只应该进行向后兼容的更新。如果我们要给项目增加新的功能和特性,就必须要考虑项目的兼容性。比如接口增加了一个新的参数,为了老版本客户端能顺利访问这个接口,服务端应该考虑为老版本客户端缺少的请求参数提供一个默认值。我们也可能会在响应中添加新的属性,或者提供一些新的接口,当然这些一般不会影响老客户端。必须进行不兼容的升级。有时我们不得不进行一些不兼容的升级,并对API进行一些重大更改。考虑到微服务之间的松耦合,我们不能强制客户端立即升级。这时候,我们可以考虑在一定时间内,两个版本的API共存。当多个API并存时,一个比较简单的方法就是在设计API的时候加上版本号,比如/v1/xxx或者/v2/xxx,但是这种写法有个小瑕疵,就是在version后面加上number已删除,此路径看起来不像是完美的REST路径。所以这里还有另外一种解决方案,就是将请求的API的版本号写入到请求头中。具体实现思路如下:首先,在微服务中,我们所有的请求一般都会经过网关。我们可以在网关中提取请求头的Accept参数,然后根据Accept中的请求版本号进行不同的请求。请求转发,如果版本号是1.0,则转发到1.0的服务;如果版本号是2.0,就会转发给2.0的服务。基本上就是这样。以微服务中的主流网关SpringCloudGateway为例,我们可以配置如下:发现:定位器:#启用:真小写服务ID:真路由:-ID:v1_provideruri:lb://providerpredicates:-Path=/p/**-Header=Accept,.*;?version=1\.0(|;.*)filters:-StripPrefix=1server:port:8082我们来看看这个配置:首先记得关闭自动服务发现,否则通过默认服务名代理是走不通的我们的配置过滤掉。然后我们手动配置服务转发。以上配置基本上是常规配置。与版本号相关的配置为Header=Accept,.*;?version=1\.0(|;.*)。这个配置是针对request首先,前面的Accept表示这里要判断请求头中的Accept字段,后面是value(两者之间用,隔开),这个value是一个正则表达式。*;?version=1\.0(|;.*),意思是version=1.0前后可以有任意字符串,只要value中包含version=1.0的都会匹配。只有匹配到才会转发请求,否则不转发请求。最后,我们在发送请求的时候,设置如下请求头即可:如果版本号为version=2.0,会报404错误:好了,一个小版本号的话题,有兴趣的朋友可以试试最后一段代码~
