得物API元數據中心探索與思考
一、背景
目前市面上針對API的管理平臺很多,但由于各種客觀因素,這些平臺的功能都更多聚焦在API文檔的消費側。而對于API文檔的生成都非常依賴開發人員的手動創建,很難保障文檔的實時性和有效性。市面上常見的API管理平臺,由于缺乏有效的管理機制,完全依賴開發人員的主動維護,在文檔體量變大之后就出現了文檔歸屬混亂、文檔重復上傳、文檔信息更新不及時等問題。
圖片
由于文檔缺乏有效的維護,很大程度上局限了API文檔在消費側的作用。舉個例子,如果一份API文檔更新不及時,那么前端就很難基于過時的文檔進行數據Mock。如果平臺大多數的文檔都存在更新不及時的問題,那其他的平臺也很難把平臺的API文檔作為有效信息使用。
二、Mooncake API文檔維護
為了解決文檔的維護問題,得物技術部自研了Mooncake平臺,并從文檔組織規范、文檔生成效率等方面做了大量的嘗試。
API文檔組織規范
平臺用戶對于接口文檔的存儲管理、交付時間和交付質量均有一定的訴求。平臺通過規范的方式統一起來,建立接口文檔項目和目錄組織規范,降低接口查找難度和用戶使用費力度。
規范應用名稱
如果應用名稱可以任意創建,從技術部現有數據看來,各域定義的巨大差異將會導致用戶使用存在一定的理解成本。為統一項目命名規范,同時更清晰的展示接口與項目之間的關系,平臺計劃與發布平臺&CMDB&網關等系統保持一致,統一采用CMDB中的應用名作為項目名稱,降低文檔查找的難度。
通過打通CMDB數據,統一CMDB應用名,打通與公司內部平臺的數據,主要包含:
- 建立與發布平臺的關系,自動獲取應用染色環境列表,降低接口調試難度;
- 建立與Gitlab平臺的關系,自動獲取應用需求迭代數據,降低文檔與需求綁定的費力度;
- 建立與網關平臺的關系,一鍵同步接口網關自動關聯路由組等數據信息;
- 打通交易網關、APM的數據,獲取接口文檔信息,豐富文檔信息密度。
通過將應用名稱規范化,Mooncake平臺建立了一個規范化的應用命名體系,讓用戶可以更方便地查找和使用文檔,并提高了團隊的協作效率和產品質量。
規范文檔分類
如果以類名或注解作為文檔分類的依據,導致文檔的可維護性逐漸降低,文檔和業務的關系也逐漸削弱。為了解決這個問題,平臺通過規范文檔的分類,降低文檔的查找和管理難度。
- 在技術層面上,提供多級分類能力,規范化維護文檔分類,并完成商家、客服、供應鏈、交易等規范性分類的推動以及約束文檔的落地;
圖片
- 在規范層面上,推進各個團隊根據自己的業務場景按照統一的規范來分類文檔,從而提高文檔的可維護性和管理效率。例如,推動客服、商家、交易等各個域落地接口目錄規范文檔,項目負責人或Owner定期檢查分類規范的執行情況,并對分類不規范的文檔進行整理和優化。
通過技術和規范手段相結合,規范文檔分類,可以降低文檔的查找難度,提升文檔的可維護性和管理效率。
API文檔生成
MooncakeUpload Idea插件
得物技術部研發的MooncakeUpload Idea插件可以幫助解決API文檔創建和錄入的問題。該插件通過解析Java項目里的注解和注釋,實現了一鍵生成API文檔的功能,降低了API文檔創建的費力度。相較于手動創建接口文檔,使用插件上傳API文檔所需的時間僅為幾秒鐘,而且規范了接口的分類屬性,使得上傳文檔過程更加簡便和快速。在每個迭代中,使用插件可以節約將近667小時的時間。
實現原理
基于IntelliJ Platform自身的基礎架構,依靠PSI(Program Structure Interface)核心特性,通過分析解析出來的語法樹可以獲取準確的代碼信息,例如獲取文件中包含的類、方法、字段和注釋等信息。
核心實現
- 配置信息
通過IntelliJ Platform提供的虛擬文件系統(Virtual File System)功能,讀取插件的信息配置,主要包括Mooncake的項目信息,人員的域賬號等。從而能夠獲取Mooncake的分類數據,以及接口的變更人員。
// 解析misc配置文件
File miscFile = new File(editor.getProject().getProjectFile().getPath());
Element miscElement = JDOMUtil.load(miscFile);
// 讀取token
public static String getToken(Element element, PsiFile psiFile) {
try {
String token = getHistoryConfig(element, psiFile, MooncakeConstant.HistoryToken);
if (token.equals("")) {
token = getProjectConfig(element, psiFile, MooncakeConstant.Token);
}
if (token.equals("")) {
Messages.showErrorDialog("請先去idea/misc.xml配置MooncakeUploadApi配置", "獲取配置失敗!");
}
return token;
} catch (Exception e) {
Messages.showErrorDialog("請先去idea/misc.xml配置MooncakeUploadApi配置", "獲取配置失敗!");
return "";
}
}
原有的配置功能,會通過用戶配置的項目名稱信息和當前路徑進行二次校驗,增加了用戶理解的難度,平臺插件使用的問題中,80%的問題來源于配置的繁瑣。因此在2.0版本之后,通過內置數據校驗,降低了項目信息的配置難度,配置信息僅需一個參數即可:
<component name="mooncakeUpload">
<option name="token">xxxxxxxx</option>
</component>
- 出入參信息
依靠PSI核心特性,通過解析選中文件的語法樹,提取字段信息,組裝API文檔的出入參和注釋,主要核心邏輯:
// 獲取偏移量
PsiFile editorFile = e.getDataContext().getData(CommonDataKeys.PSI_FILE);
PsiElement referenceAt = psiFile.findElementAt(editor.getCaretModel().getOffset());
// 獲取選中的類或者方法
PsiClass selectedClass = PsiTreeUtil.getContextOfType(referenceAt, PsiClass.class);
PsiMethod selectedMethod = PsiTreeUtil.getContextOfType(referenceAt, PsiMethod.class);
// 獲取選中類下的所有方法
PsiMethod[] psiMethods = selectedClass.getMethods();
// 獲取類上的注解
String apiValue = PsiAnnotationSearchUtil.getPsiParameterAnnotationParam(selectedClass, SwaggerConstants.API, "tags");
// 獲取參數所屬類
PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(psiParameter.getType().getCanonicalText(),
GlobalSearchScope.allScope(project));
// 獲取參數所有字段
PsiField[] fields = psiClass.getAllFields();
// 字段源類型,可以獲取所有信息
PsiType type = field.getType();
// 字段名稱
String name = field.getName();
- 可視化面板
Mooncake平臺支持API文檔的多級分類,為了降低接口文檔的分類難度,降低對代碼的侵入,基于Java的Swing GUI庫,我們提供了可視化操作面板,用戶可以選擇需要上傳的接口和分類信息,以及需求信息。
圖片
- 版本更新
MooncakeUploadApi上傳插件是得物技術部自主研發的插件,由于存在公司的業務信息,無法上傳到插件市場,只能將插件打包成Jar文件給開發使用。這種情況下,可能會出現以下問題:
- 用戶無法及時感知到插件修復過的版本,導致升級新版本時存在困難。如果出現問題,用戶還需要找Mooncake維護人員定位問題,并進行手動修復和更新,維護成本比較高;
- 由于不能上傳插件市場,用戶升級插件需要手動找到Mooncake維護的插件文檔,并下載相應的Jar包進行更新,費力度高。
針對以上可能存在的問題,得物技術通過搭建私有倉庫方式,最終實現了插件更新方案,如圖所示:
圖片
最終實現了插件啟動的自動檢查版本更新,并進行通知。
圖片
結果
通過自研Mooncake Idea上傳插件,實現Mooncake平臺以下收益:
- 快速響應并定制化需求:通過打通Gitlab,插件可以根據代碼分支來自動綁定接口的業務需求,以便快速響應相關問題;
- 提升開發效率:使用插件可以大幅度降低API文檔創建的成本和負擔,從而讓開發人員更加專注于代碼的開發和測試等任務中;
- 規范API文檔:插件通過可視化面板交互方式,規范了文檔的格式和內容,并能夠快速選擇上傳的文檔分類和字段信息,以便更好地管理和使用API文檔信息,從而提高了規范化程度。
目前研發部門接通過插件每個迭代上傳API文檔的次數(如圖所示),平均每個迭代產生數千次的變更,大大提升了維護文檔效率,達到降本提效的目的。
圖片
基于Gitlab MR自動解析
背景
圖片
平臺提供了API Upload插件之后,整個文檔生產端現狀如下:
- 服務端在開發階段通過手動/插件上傳API文檔到Mooncake側;
- 提測節點時從網關同步當前迭代新增的API接口,與Mooncake側接口比對,查看是否存在,不存在則要求開發上傳接口。
通過網關來進行新增接口卡點,可能存在以下問題:
- 網關側配置的接口僅為需要走網關流量的接口,不走網關的接口,如Dubbo接口、內部接口并不能保證接口在Mooncake存在;
- 即使在Mooncake存在的接口,如果在上傳之后又產生了變更,通過網關的卡點并不能保證最新的變更也同步到了平臺。
因此,平臺在通過上傳插件降低API生成費力度的同時,也需要將現有的研發流程仍然強依賴使用者的習慣、API文檔的質量不穩定的風險考慮進去。
針對這個問題,平臺在Gitlab的流水線中,新增了一個自動解析代碼的節點。對于每個向release分支合并的MR,將其中和接口定義相關的部分進行解析并自動在平臺生成/更新對應的API文檔,從而保證所有接口在發布前一定將最終的接口定義同步到Mooncake平臺。
實現
圖片
- 配置Gitlab流水線任務
自動解析需要拉取項目的全量代碼和依賴包代碼,因此占用的內存空間較大;
自動解析項目耗時較長,例如公司內部某個項目,7k+的文件需要耗時3.5min,自動解析feature分支消耗太多資源。
因此我們最終針對HTTP接口只做每個迭代的兜底,通過解析Release分支,保障每個迭代結束時,文檔都是完整的和最新的。
- 獲取二方包源碼
由于二方包在編譯為Jar之后,代碼的注釋會丟掉,而API文檔需要解析字段的注釋和注解來描述字段的含義,自動解析要保證接口的完整性,需要補全二方包的注釋。因此我們掃描了Pom文件的依賴包,并將公司的二方包全部下載到當前項目目錄里面,并反編譯解析原始數據。
獲取公司內所有的二方包源代碼數據:
JSONArray allModuleDepsTreeData = new JSONArray();
for (String fileDepTree : arrayListScannerMgr_Dep_Tree_POM) {
JSONObject treeDependeces = dependcesParse(fileDepTree);
allModuleDepsTreeData.add(treeDependeces);
}
// 過濾公司二方包
String group = nodeChild.getGroupId();
if (group.contains("xxx")
|| group.contains("xxxx")
|| group.contains("xxx")
|| group.contains("xxxx")) {
return true;
}
// 下載所有二方包
File tempFile = new File(jarScanPath.trim());
String fName = tempFile.getName();
fName = FilenameUtils.removeExtension(fName);
fName = fName.replaceAll("-", "_");
- 解析項目所有的代碼
通過調研,Qdox工具包具備體積小,解析效率快,使用文檔簡單的特性,因此采用使用Qdox將項目中的所有代碼解析為Java語法樹,并實現API文檔的信息提取。
核心邏輯如下代碼所有,解析所有class,并基于Swagger注解和RestController注解提取所有的Http接口。
// 初始化builder
JavaProjectBuilder builder = new JavaProjectBuilder();
builder.setEncoding(StandardCharsets.UTF_8.name());
// 讀取所有class信息
builder.addSourceTree(new File(sourceDir));
Collection<JavaClass> classes = builder.getClasses();
// 過濾所有http接口
// 獲取所有http api class
Collection<JavaClass> httpClasses = new ArrayList<>();
for (JavaClass javaClass : classes) {
if (CommonHelper.isHttpClass(javaClass)) {
httpClasses.add(javaClass);
}
}
而接口文檔信息的解析與Idea插件解析思路基本一樣,最終將所有的接口方法解析為JSON格式的API文檔,如圖所示:
圖片
- 將解析的接口數據與Mooncake平臺數據對比
- 接口在Mooncake不存在的,直接上傳到Mooncake平臺,保證API文檔的完整性;
- 接口存在的,比對接口文檔核心數據,包含出入參和路徑等,不一致則更新接口文檔,保證API文檔的一致性。
三、Mooncake API元數據中心
Mooncake平臺通過不斷完善從生產到消費鏈路的信息,延長API文檔的生命周期,完成API文檔元數據中心的閉環。在平臺的探索過程中,逐漸沉淀了豐富API文檔信息,解決了API的利用率低,API信息密度低的問題。目前,API信息主要包含以下:
- API描述信息:如 Swagger、OpenAPI 等格式的 API 描述文件,包括 API 名稱、版本、路徑、參數、響應等;
- 接口規范:定義API請求和響應協議,規范接口分類;
- 穩定性:API的版本管理、生命周期、周期變更率等數據;
- 文檔和示例:API的使用文檔、示例代碼、調用等;
- 開發平臺:提供API開發者所需的工具和SDK,例如IDEA插件、Go cli等;
- 研發流程規范:提供接口版本周期穩定性數據,例如:調試是否成功、自動化測試用例等。
通過打造得物API元數據中心,更有效的提高API開發和管理效率,使得API能夠更加透明化、可靠化、易于使用。基于豐富的API文檔信息,平臺圍繞調試、Mock、數據開放等API消費側的功能也做了大量的探索嘗試。
調試
由于平臺沉淀了精確的接口字段定義,因此基于這些定義對接口進行調試自測就非常方便。在提供基礎調試功能的同時,平臺也通過以下手段對調試的體驗進行了優化:
- 基于文檔信息,自動填充入參字段信息;同時基于文檔信息進行簡單的類型校驗;
- 通過同步CMDB應用名稱,自動獲取染色環境名稱,支持調試自動填充染色環境參數;
- 打通內部核心平臺,優化接口簽名和鑒權問題,降低接口調試難度。
對于部分仍然習慣于使用postman進行調試的用戶,平臺也支持將postman調試記錄進行一鍵同步。
之所以這么執著的推動大家到Mooncake來進行調試,主要是期望將調試記錄作為研發完成自測的一種“憑證”,并將其同得物現有的研發協同面板系統進行結合,把“自測憑證”作為生成前后端聯調單的前置條件,通過保障聯調過程中的接口質量從而提升聯調效率。
整個聯調過程大致如下:
圖片
Mock
- 由于項目接口的完整性和及時性,前端可以基于平臺的Mock功能,在開發階段,前端可以Mock需求下的所有接口,充分完成功能的自測,前置聯調流程,降低因為后端延遲提供接口的帶來延期的風險。
- 基于入參字段的準確性和完整性,在Mock數據過程中,平臺可以校驗入參的信息的準確性,包括是否完整,數據入參類型是否準確等,提前發現問題,提升前端交付質量。
- 通過API文檔的調試功能,平臺沉淀了基于接口文檔的真實數據,因此,平臺可以自動為前端提供更加精準的具備業務屬性的數據Mock,以及不同的異常狀態碼數據場景,更加真實對頁面場景進行還原。
圖片
API元數據平臺
圖片
將公司所有的API文檔收斂到Mooncake平臺,通過保障接口的完整性和及時性,可以保障所有消費平臺都能拿到接口的詳細信息,同時通過與其他平臺協作,將接口的不同維度信息收斂到平臺信息,例如接口的等級、讀寫屬性等,豐富文檔的信息密度。
通過提供OpenApi,為公司內部平臺提供API信息,例如:
- 提供API文檔包括接口名稱,字段語義,出入參完整性給APM監控平臺,提升Trace鏈路的可讀性;
- 提供完整的出入參信息,與流量平臺的接口數據比對,及時發現接口版本問題;
- 提供接口的變更率、是否調試成功等數據給提測平臺,作為質量管控數據面板的一部分;
- 提供接口相關聯的域名信息給網關平臺,作為網關平臺監控接口流量信息的依據;
- 將接口信息提供給接口自動化平臺,可以提升測試編寫接口測試用例效率。
四、展望
目前得物面對日益增長的業務,尤其是涉及分布式架構、微服務等技術和架構時,通過API元數據中心集中化管理API接口文檔,在協同管理各團隊、保證接口的一致性和完整性、快速演進變更、降低溝通成本等方面有著至關重要的作用。后續,平臺依然會圍繞已經沉淀的API信息,在接口自動化測試、API文檔管理、接口健康度監控等上下游領域進行持續的探索。