一、前言
随着微服务化在携程的全面落地,业务被拆解得越来越细,接口数量和内外部调用方不断增多;另一方面,随着产品迭代的不断增速,对接口的修改也变得愈加频繁。
接口契约,作为各端的沟通桥梁,在微服务时代显得尤为重要。如何管理好不断变化的接口契约,是携程机票 BU 在微服务化过程中遇到的一大难题。本文结合一些契约管理的实践经验,介绍下携程契约管理的演进,以及携程机票 BU 自研发的契约系统(简称 MOM - model object management,以下都以此代替)。
二、携程契约管理演变史
2.1 线下管理契约
在携程机票 BU 以及其它的一些团队里,早期的契约管理,主要由程序员线下进行。当发生需求变更,需要增加新的接口,或改动原有契约时,由相应的开发线下编辑契约文件(如 XSD 文件),再生成指定格式的契约(如 Java 类,Protobuf 文件),提供给使用方。
在单体应用时代,或业务没有拆分很细的时候,接口契约并不会太多,调用方也相互熟悉,只要有专人负责线下契约维护,这种管理方式并不会有太大问题。但随着微服务化,接口和契约不断增多,此种管理方式的瓶颈逐渐凸显,问题主要集中在以下几点:
1)线下管理,契约容易丢失。
2)契约变更,只有编辑者才能主动感知,无法很好的周知到所有使用方。若契约变更不当,容易在上线时才发现问题。
3)当有多个需求,需要对同一份契约进行修改时,需手工解决各种冲突,这个过程极易出现问题。就好比不用 Git,手工去进行代码分支管理一样。
4)契约可读性差,接入沟通成本大,各团的原始契约也没有一个统一的标准。
2.2 第三方工具管理契约
由于线下契约管理,存在太多各式各样的问题。携程内部各团队都在积极探寻,尝试借助一些第三方工具,去更好地管理契约。常见的有 Swagger、Knife4j、YAPI 等工具。
Swagger 是一款非常受欢迎的开源框架,它通过文档与代码的绑定,能很好地对 API 进行文档化描述,接入起来也比较方便。Swagger 一定程度解决了线下管理契约易丢失、可读性差等问题。但由于 Swagger 单机或集群的契约管理方式,会使得携程内部各项目(应用)之间,显得比较割裂,所以 Swagger 并未在携程内部大范围推广。同时,Swagger 的界面比较粗糙,文档的编写需要遵守一系列的规范,上手有一定难度。
Swagger
Knife4j 是一款国产开源项目,也是用于 API 文档的生成。与 Swagger 相比,它有着更加出色的界面,并且能够支持如离线文档、安全控制、在线调试等功能。但是与 Swagger 有相同问题,Knife4j 并不是一个中心化的契约管理方案。
Knife4j
YAPI 是去哪儿为推进 API 标准化,研发的一款 API 治理工具。本身也拥有丰富的功能,携程内部也有一些团队在使用,但它的设计更注重于 API 的多样化管理,不支持模型共享,不支持单独模型聚合方式管理,必须要有接口名称和输入输出信息。这些限制给契约的管理带来不少麻烦。
YAPI
总的来说,Swagger、Knife4j 和 YAPI,将契约管理方式从线下转到了线上,一定程度解决了契约易丢失,可读性差的问题,同时还提供一些 API 管理的高级功能,如调试、Mock 等,那为什么还需要 MOM 呢?
2.3 为什么研发 MOM
携程机票 BU 结合自己的实践经验,并走访收集了其它团队在契约管理上的痛点,总结出一款好用的契约管理系统,所应当具备的功能有:
1)云端管理,项目、权限管理
2)友好的界面,支持在线编辑
3)版本管理,差异比对
4)契约变更消息通知
5)契约导入,代码生成
6)模型共享
由于 Swagger、Knife4j 以及 YAPI,对以上功能的支持或多或少有缺失,而且它们都是以面向 API 管理为主,并不能很好地解决上述问题。携程机票 BU 开始尝试研发 MOM,并在近一年内陆续在携程内部推广开来。
契约系统又称为 MOM(model object management),从名字就能看出,其管理的是模型对象。这个设计理念也是与 Swagger,YAPI 等工具面向 API 做管理的最大不同。
模型,无关于接口实现,无关于契约文件类型,也无关于具体生态环境,它仅由字段组合而成。在 MOM 中,模型作为被管理的最小单位,可以进行任意的组合与嵌套,通过这种结构化的管理方式,使得契约的描述性更加强大,突出表现在能够模型共享上。
模型管理这一理念,其实也是源于携程机票 BU 的实践经验。在 2017 年左右,携程服务端的技术栈从.Net 转向 Java,前端有 iOS、Android、React Native、Hybrid 等技术栈,一个接口对应了多份契约文件。同时,随着微服务化的深入,接口数量也暴发式增长,我们发现很多接口契约的部分内容是可以复用的,例如航班信息,主要就包含了航班号,出发到达地,机型等信息。
为了解决一份契约会有不同的文件格式,以及契约复用的问题,MOM 设计之初就抽象出模型这一概念,并将其作为底层设计的核心思想,这也使得 MOM 之后具有良好的扩展性,所有其它工具支持的功能,理论上 MOM 都能支持。
总的来说,Swagger,Knife4j 和 YAPI 主要关注的都是对 API 的管理,而 MOM 更加关注的是对契约的管理。
三、MOM 架构
MOM 作为一个帮助大家管理契约的工具,在使用频率方面,并不会太高,并发压力不会太大。研发时更侧重于其功能的丰富性。所以,整体的物理架构不是很复杂,但支持的功能以及逻辑单元相对丰富,且留有很好的扩展性。
3.1 物理架构
MOM 采用双集群进行灾备,使用 MySQL 管理模型、项目、版本等结构化数据,Mongo 用于管理用户上传的原始文件及最终生成的契约文件。系统采用 Local Cache + Redis 的二级缓存结构进一步降低 DB 的访问压力,同时用 Zookeeper 保证各台机器的数据一致性,可能发生变更的静态数据由 Schedule 定时进行更新。整个系统使用传统的 MVC 架构进行设计,前端页面使用 Angular 搭建,服务使用 Java 进行开发。
3.2 逻辑单元
从以上的逻辑单元可以看出,MOM 主要包含的功能有:项目管理,契约编辑,多版本管理,契约生成,变更通知,以及底层的核心:模型管理。后面会对这些功能进行展开介绍,分享这些功能的相关经验。
四、MOM 功能介绍
4.1 契约编辑
契约编辑主要分为两块,契约导入和在线编辑。
4.1.1 契约导入
契约导入,顾名思义是将现有的契约,可以是各类契约文件,导入到系统中, 方便后续的查看和修改。这功能的必要性有一定历史原因,因为各团队维护契约文件的方式都不相同,契约文件类型存在差异,要想后续都在 MOM 上进行管理,就必须提供这么一个功能方便各方快速接入。
之前也提到过,MOM 的核心理念是管理模型,不是具体的接口或契约类型,这一层抽象使得契约导入功能能够很方便的实现,需要支持什么类型的契约导入,开发其相应的文件解析器,将契约文件转换成 MOM 底层维护的模型即可。
契约导入完成后, MOM 界面上统提供树形结构与扁平化的展示方式,方便用户直观看到契约模型的相关信息。目前,MOM 支持 BJSC(携程 SOA 契约),Protobuf,XSD,Json Schema 等契约文件的导入。后续若要支持更多契约类型,也能很快地进行扩展。
4.1.2 在线编辑
在线编辑是 MOM 的特色之一,对于历史契约,在用户完成契约导入后,MOM 会将用户的契约模型持久化下来。对于新接口新契约,MOM 也支持在界面上直接进行创建。后续,当接口需要扩展功能,修改契约时,MOM 支持在界面上直接修改并保存。界面上友好的契约修改操作流程,避免了人工编辑契约文件可能带来的问题。
值得一提的是,对契约的编辑修改有一项 Best Practice:对于已发布的契约,后续的修改只能是顺序增加字段,不能修改现有字段的名称和类型,也不能在任意位置插入新字段。因为,修改已有字段的名称和类型,必定会带来线上兼容性问题;要求顺序增加字段,主要是针对像 Protobuf 这类对顺序有强要求的契约格式。那字段越加越多,越来越冗余怎么办?可以考虑另起炉灶升级接口契约,或尝试 GraphQL。
4.1.3 契约编辑小结
契约导入和在线编辑是 MOM 的一大特色。这功能有别于其它工具,因为 Swagger、Knife4j、YAPI 都是面向 API 做管理,或与代码绑定不支持导入编辑,或只支持从 Postman 等工具导入接口。这与 MOM 专注契约的管理,有本质上的不同。契约可以看作是 API 的进一步抽象,MOM 导入和编辑的是契约,而不是 API。
当契约导入后,或完成编辑保存后,数据是以模型进行管理,在 MOM 上,模型到契约间的转换是双向的。举个例子,导入的是 XSD 格式契约,MOM 将其转成模型进行存储,最终在界面上,MOM 又可以将模型转换成各类契约格式,进行展示。
4.2 项目管理
MOM 中的契约模型,是以项目维度进行管理,项目分为应用与自定义项目。
应用是携程内部的定义,可以包含一组接口,通常也是生产发布的最小单位。通过这种直接与应用一一绑定的方式,可以方便用户快速查找自己应用的契约。
自定义项目与应用的差异,主要在于管理模型的差异上。自定义项目更多用于,存储共享模型、数据埋点模型等, 它们可以脱离携程的生态环境而存在。值得一提的是,模型在不同的项目下是相互隔离的,原则上一个项目只能使用该项目下的模型,如果想要跨项目使用模型,必须先建立项目之间的绑定关系。
为了降低契约被项目成员之外的用户误操作,MOM 提供了权限管理,权限实际是建立在应用维度之上的。MOM 会定时同步应用权限(谁是 Owner、管理员),只有拥有这些角色的用户才允许编辑该项目下的契约模型。
4.3 模型管理
模型管理,是 MOM 底层的核心。模型是最小的管理单元,契约的节点树是在此基础上进行嵌套搭建的。MOM 维护了基础数据类型,除此之外系统允许用户定义自定义模型和枚举类。
MOM 参考了 Java 的设计,项目以文件夹形式进行管理,模型所处的文件路径,决定了模型最终的生成位置。为了更好的进行模型描述,MOM 对模型抽象出了模型名称、字段、描述、注解等相关属性,通过构建绑定关系使得共享策略得以生效。
契约系统的模型共享,主要分为项目间的共享与外部 jar 包内的模型共享。项目间的主要使用场景是,用户构建自定义项目维护共享模型,该模型可以被其他项目中的接口访问,来避免触发冲突规则。外部 jar 包引用,是通过同包下的同名模型进行相关的替换操作,系统解析 maven 仓库中 jar 包中的原始文件, 提取类型的节点树信息,替换项目中的模型。
4.4 多版本管理
多版本管理是契约管理的一个重要组成部分。
之前提到,由于需求的快速迭代,团队协作中,契约不可避免的产生多个可编辑的版本。MOM 为多个版本提供了相互隔离的环境,并且提供回滚,增量覆盖全量覆盖的相关功能。需要注意的是,版本在发布之后是不允许进行编辑操作的。但是多版本隔离的同时会带来冲突的合并问题,如何解决多版本冲突,提升契约的稳定性更是重中之重。为了解决稳定性问题系统先后提供两种解决方案,其一是版本比较,其二是版本冲突的自动合并。
4.4.1 多版本比较
如何帮助用户更快的发现契约的变更,除了契约变更通知这一种手段外,系统自定义了相关的比较规则,降低用户比较的费力度。核心思路是减少比较内容,突出差异信息。
因为契约的生成格式可以是多种多样的,如果是对每一种生成格式都提供一套比对方案虽然能满足各方的需求,但是必然给系统的维护带来一定的挑战。前文提到系统使用结构化的模型管理,那么是否能提供一种可读性良好,同时能突出展示差异的解决方案?答案是有的。
结合目前线上在使用的契约,参考多方的契约处理格式,最终系统仿照 Protobuf 的文件定义出模型及接口的比较模板,针对用户关心的主要数据,提取出:字段名、字段别名、注释、模型等相关属性。使得用户比较时可以更直观的发现契约的改动。
当然系统也提供了生成文件的比较,如 Java 文件,方便特定的用户进行比较。如果有需要,后续也会对其他的格式逐步增加比对支持。
4.4.2 版本冲突自动合并
版本比较一定程度上帮助用户发现问题,但冲突合并才是解决问题的关键。能否支持冲突合并,成为考量系统易用性的重要标准。
契约的冲突合并,与代码的冲突合并类似,每当契约需要进行正式发布时,都应检查在其之前是否已有其它修改发布了。若有,则需解决可能引入的冲突。冲突的类型可能是新增类型,或者对某个模型的字段的修改,这些变更如果遗漏,显然是致命的。
针对该问题,系统参考 Git 的文件管理规则进行设计。契约合并的时机,发生在用户即将发布正式的契约版本时,系统自动拉取最新的发布版本与当前的发布版本进行比较,并做冲突展示,由用户选择解决冲突的方式,允许用户忽略冲突发布,当然相应的结果由用户承担。冲突的自动合并,降低了手工操作犯错的几率,极大的提升了团队协作效率。
4.5 契约生成
契约生成是大多数用户比较关心的地方。能否满足各用户的使用场景,对系统而言也是一个重大挑战。
契约系统独立维护了一套描述性极强的模型结构,对于不同的生成契约类型,只需要根据不同契约的生成规则,定制不同的契约模板即可。随着系统的不断迭代,目前已经支持 Java、TS、GraphQL、C#、XSD、Protobuf 等文件的生成。
此外,光生成契约文件对于用户来说,易用性可能还不够。MOM 还支持将生成好的契约,直接发布到代码仓库。目前,MOM 已支持直接将契约部署到 maven 和 NPM(Node Package Manager)仓库,只需简单配置仓库地址即可。至此,契约的编辑到部署,可以全在 MOM 的操作界面上完成,这极大的提高了开发效率。
4.6 变更通知
MOM 不仅方便了契约维护方对契约进行管理, 也方便了接入方能够很好的查看接入。当契约发生变更时,将变动消息告知所有的关心此契约的人,是很有必要的一个功能。
MOM 的通知包含变更通知、发布通知。通知的渠道可以是各式各样的。通知的范围包含项目的管理员、owner、项目的关注人以及项目的调用方。契约系统会把用户的每一次编辑记录进行收集作为后续的通知内容,并把契约的改动可能影响的潜在用户列入通范围。此外,通知内容上提供 ReleaseNote 的编辑方式,用户编辑完成之后可以选择需要通知的对象及通知的方式进行通知操作。
目前,MOM 的消息通知渠道,主要包括 Trippal(携程内部通讯工具) 和 Email。系统预留了可扩展性,若需其它渠道的消息通知,可以快速进行开发或配置。
五、总结
总的来说,MOM 与其它工具最大的不同,是其更关心契约的管理。特别是在敏捷模式盛行的今天,业务变更快速而频繁,不可避免的经常对契约进行修改。如何管理并支持好这些修改,是 MOM 这个产品需要解决的核心问题。
目前,MOM 在携程内部正处于推广阶段,功能也在不断的增强与完善中。后续会考虑剥离携程相关生态的依赖, 在契约导入、契约生成、消息通知等功能上, 做到可插拔可扩展。最终开源到社区,帮助大家解决契约管理的问题。
最后:
如果你维护的项目多,接口契约经常发生变化
如果你的接口文档不清晰,甚至还是线下维护的方式
如果契约模型需要在各项目,各接口间共享
如果你需要生成各式类型的契约,提供到使用方法
如果你有多个团队共同维护一个项目, 契约修改经常冲突
如果你想把契约的变更,及时通知到各个关注方
那你可以参考 MOM 以模型为中心的契约管理方案,也可以持续关注 MOM 的后续消息。
作者简介
章鱼, 携程资深后端开发工程师,契约系统创始人与核心研发。
Sylar, 携程资深研发经理,专注 Java 技术栈相关研究。
本文转载自:携程技术中心(ID:ctriptech)
评论