以模型爲中心,攜程契約系統的演進

作者簡介

 

章魚, 攜程資深後端開發工程師,契約系統創始人與核心研發。

Sylar, 攜程資深研發經理,專注Java技術棧相關研究。


一、前言



隨着微服務化在攜程的全面落地,業務被拆解得越來越細,接口數量和內外部調用方不斷增多;另一方面,隨着產品迭代的不斷增速,對接口的修改也變得愈加頻繁。

接口契約,作爲各端的溝通橋樑,在微服務時代顯得尤爲重要。如何管理好不斷變化的接口契約,是攜程機票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的後續消息。

本文分享自微信公衆號 - 一線碼農聊技術(dotnetfly)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章