服務API版本控制設計與實踐
一、前言
筆者曾負責vivo應用商店服務器開發,有幸見證應用商店從百萬日活到幾千萬日活的發展歷程。應用商店客戶端經歷了大大小小上百個版本迭代後,服務端也在架構上完成了單體到服務集群、微服務升級。
下面主要聊一聊在業務快速發展過程中,產品不斷迭代,服務端在兼容不同版本客戶端的API遇到的問題的一些經驗和心得。一方面讓團隊內童鞋對已有的一些設計思想有一個更徹底的理解,另一方面也是希望能引起一些遇到類似場景同行的共鳴,提供解決思路。
二、通用解決方案
應用商店客戶端迭代非常頻繁,發佈新的APP版本的時候,勢必導致出現多版本,這樣服務端就會導致多個不同的客戶端請求。強制用戶升級APP,可能會導致用戶流失,因此採用多版本共存就是必須的。以下是業界討論過的的一些SOA服務API版本控制方法參考[1]。在實際開發中原則上離不開以下三個方案。
方案一:The Knot 無版本——即平台的API永遠只有一個版本,所有的用戶都必須使用最新的API,任何API的修改都會影響到平台所有的用戶。(如下圖1)
方案二:Point-to-Point——點對點,即平台的API版本自帶版本號,用戶根據自己的需求選擇使用對應的API,需要使用新的API特性,用戶必須自己升級。(如下圖2)
方案三:Compatible Versioning——兼容性版本控制,和The Knot一樣,平台只有一個版本,但是最新版本需要兼容以前版本的API行為。(如下圖3)
(引用自:The Costs of Versioning an API)
簡單分析,The Knot只維護最新版本,對服務端而言維護有一定簡化了,但是要求服務使用者及時適配最新的版本,這種做法不太適用用戶產品,目前內部服務比較適用。Point-Point針對不同客戶的版本提供獨立的服務,當隨着版本的增加開發和運維的維護成本會增加,這種在後面我們面對「協議升級」的時候有使用。
方案三應該是最常用的情況,服務端向後兼容。後面案例也主要採用這種思想,具體的做法也是有很多種,會結合具體的業務場景使用不同策略,這個會是接下來討論的重點。
三、具體業務場景面臨的挑戰和探索
3.1 The Knot 無版本和Point-to-Point模式的應用場景
上圖是我們應用商店迭代變化的一個縮影,業務發展到一定階段面臨以下挑戰:
1)業務發展前期,作為服務提供方,服務端不僅要支撐多個版本應用商店客戶端,同時服務於軟件側的PC助手;
2)產品形態變化多樣,服務端接口變更和維護面臨多版本客戶端兼容的挑戰;
3)架構邏輯上,服務端採用早期傳統架構,開發和維護成本比較高;服務端與客戶端進行交互的協議優化升級;以及服務拆分勢在必行。
所以服務端協議、框架升級以及公共服務拆分是首要解決的方向。改造經歷了兩個過程:
-
階段一新版本新的接口一律採用新的JSON協議;已有功能接口進行兼容處理,根據客戶端版本進行區分,返回不同協議的格式內容。
-
階段二隨着業務迭代,新的版本商店依賴的所有接口都完成了協議升級後,為了提升服務的穩定性,舊的協議性能無法明顯提升,一方面升級後端架構和框架,提升開發效率和可維護性。同時拆分和獨立新的工程,實現歷史工程只提供給歷史版本使用。我們針對大流量高並發、以及基礎服務場景比如首頁、詳情、下載進行獨立服務獨立拆分。同時也提取一些公共的內部RPC服務,比如獲取應用詳情、過濾服務等。
經過改造,服務端架構如上圖所示。
1)至此Old-Service後續只用進行相應的維護工作即可,對應Point-to-Point版本。
2)內部的RPC服務由於只提供內部服務,服務端和客戶端可以隨時同步升級,只要維護最新的版本就可以,採用The Knot模式。這裡需要注意的是服務的升級需要注意保持向下兼容,在擴展字段或者修改字段的時候需要特別小心,不然可能在服務升級的時候會引起客戶端調用的異常。
3.2 Compatible Versioning:兼容性版本控制
兼容性版本控制應該是最常見的版本控制方式,特別是在C/S架構當中,具體的兼容性版本不同的策略總結有API版本、客戶端版本號、功能參數標誌等。
場景一:API版本號控制
隨着互聯網發展的,用戶體驗要求也是越來越高,產品形式也會隨之每年有不一樣的變化。除了避免審美疲勞外,也是在不斷探索如何提升屏效、點擊率和轉化。就拿應用商店首頁列表舉例。
應用列表在形態上經歷過單一的應用雙排 -> 單排 -> 單排+穿插的布局。內容上也經歷了不同商業化模式、人工排期到算法等演進。
每個版本接口內部邏輯變化是十分大的,有明顯差異。如果只是簡單在service層根據版本進行判斷處理,會導致處理邏輯會變得異常複雜,並且還可能導致對低版本產生影響。同時商店首頁是十分重要的業務場景,結合風險考慮,類似這樣對場景,在接口URL上新增版本字段,不同對版本使用不同的值,在控制層根據不同的版本進行不同的處理邏輯會更加合理,簡單有效。具體策略也有比如在URL上新增接口版本字段/{version}/index、請求頭攜帶版本參數等。
場景二:客戶端版本號控制
類似首頁列表,商店的穿插Banner也經歷了多個版本的迭代。如下圖所示。這些穿插樣式都是在不同版本下出現的,在樣式布局,支持跳轉能力等方面各個版本的支持程度不一樣,接口返回時需要進行相應的處理適配、過濾等處理。
這類場景如果採用場景一的方案升級新的接口也能夠解決,但是會存在大量重複代碼,而且新增接口對於客戶端接口改造、特別是一些接口路徑會影響到大數據埋點統計,也是有比較高的溝通和維護成本在裏面。
為了提升代碼復用性。使用客戶端版本號控制是首選考慮策略。但是需要注意,如果只是簡單的在代碼層面根據客戶端版本號進行判斷,會存在以下問題需要考慮:
1)代碼層面會存在各種判斷,造成的代碼可讀性差,有沒有更加優雅的方法;
2)存在一個客觀情況。那就是客戶端的版本號是存在不確定性的。由於客戶端採用火車發佈模式 參考[2],多版本並行開發,導致版本號存在變動、版本跳躍不連續的情況時有發生,也給服務端開發帶來了不少困擾。
如何思考解決這些問題呢?其實對於不同的產品形態涉及的一些資源或者產品模塊本身出現在不同的迭代周期,可以認為他們具備了版本或者時間的屬性。站在程序員視角,把某個資源支持對應的客戶端版本作為這個資源對象的一個成員屬性。每種資源具有這種屬性後,也有相應的邏輯行為來對應成員方法—根據屬性進行過濾。這樣的設計賦予資源了屬性和行為後,資源具備了統一的、靈活的過濾能力,而不再是簡單的硬編碼根據版本進行if-else判斷。
有了方案後,實施起來就比較容易了。開發分配資源ID,並且設置對應支持客戶端版本範圍。過濾邏輯統一到資源對象。
代碼層面可以將過濾邏輯統一封裝到一個工具類(示例代碼),在各個業務接口返回進行過濾。更加優雅的方案是建立統一的資源上層類,封裝資源過濾方法,所有資源位的資源對象實現該上層類,統一在獲取資源邏輯完成過濾能力。
場景三:新增功能標識參數
應用商店業務主要提供用戶發現和下載新應用、更新手機已安裝的應用。商店有增量更新可以減小更新包體積,因此也叫省流量更新,有效提升用戶體驗。前期我們使用開源的增量算法,但是發現該算法在部分機器合成拆分包會耗時很長,甚至引起crash。
於是項目組尋求更加高效拆分算法。類似在這些已有接口的進行功能增強的場景,除了提供新的API或者內部簡單通過客戶端版本判斷進行擴展外,有沒有更好的方案呢?因為除了這些方案已知的弊端外,需要從長遠考慮,比如前面提到的算法,後續還會不會存在升級的可能,下載接口會不會有更多能力的增強。
結合上面思考,在原來接口基礎上新增標誌參數字段,表示該請求發出的客戶端支持的能力。為了後續擴展,字段類型為整數值,不只是簡單的boolean,服務端通過位運算完成判斷邏輯。客戶端也擺脫某個功能與版本的強一致性,不用去記錄某個版本具有某種能力。
四、關於接口設計的更多思考
最後補充一些踩過的坑和反思。服務端在提供接口時,不能僅僅關注接口的實現,更多的時候需要關注接口的使用方,他們使用的場景、調用時機等等。否則開發在對接口問題排查、維護花費的時間會比實際開發的耗時要多上好幾倍。
1)場景化:具體到什麼是場景化呢,拿商店客戶端的幫助用戶檢測手機安裝的應用版本是否最新的服務舉例,檢測時機是存在不同的場景的,比如用戶啟動、用戶切換wlan環境、定時檢測等。當需要進行精細化分析,哪些請求是有效的,哪些會引起集中請求時,這個時候如果請求上沒有場景區分,那麼分析將無從下手。所以在與客戶端溝通接口設計時,請帶上場景這個因素。接口設計上可參考如/app/{scene}/upgrade,定義好各個場景名稱,在路徑上帶上具體的場景,這樣對線上不同來源請求量級、問題分析都會有很大好處。
2)鑒權和服務隔離:除了場景需要考慮外,接口調用在分配時做好記錄和鑒權以及服務隔離。比如商店的部分接口服務不僅提供給客戶端,同時也會提供給手機系統應用調用。目前vivo上億的存量用戶體量,這裡要十分小心,系統應用的調用量控制不當,並發可比商店本身要大的多。首先前期與服務調用方評估溝通、做好設計,避免出問題。即使在出問題時,也要有機制能夠快速發現問題、能夠分析出問題的來源,降低問題帶來的損失。
至此上面解決問題的思路,都與具體業務以及背景有一定關係。隨着技術不斷迭代和發展,在移動端APP頁面動態性,目前業界也有了更多高效的技術方案,比如谷歌的Flutter、Weex等。這些技術能夠實現靈活擴展,多端統一,性能也能夠接近native。不僅減少了客戶端發版頻次,也減少了服務端兼容性處理成本。目前我們vivo也有團隊在使用和實踐。
技術不斷更迭,沒有最好的方案,只有最適合的方案。開發過程中不僅滿足當前實現,更多的是考慮到後續擴展性和可維護性。開發不能一味追求高端技術,技術最終服務於業務,堅持長期主義,效率至上。
五、參考資料
1、The Costs of Versioning an API
作者:vivo互聯網服務器團隊-Song jie