如何從 0 到 1 搭建十億級(jí)包裹 API Versioning ?

以下是根據(jù)現(xiàn)場(chǎng)演講所編輯的技術(shù)文章,希望對(duì)大家有所幫助。
1. Why
1.1 SaaS 產(chǎn)品的特點(diǎn)
SaaS 產(chǎn)品是提供某一領(lǐng)域的解決方案的軟件服務(wù),本質(zhì)是提供訂閱服務(wù)。
國(guó)內(nèi)大家常用的有飛書(shū)、釘釘、企業(yè)微信、騰訊會(huì)議、百度云、QQ 音樂(lè)等基于訂閱制的都可以歸類(lèi)為 SaaS,國(guó)外常用的 SaaS 有 Google Doc、Zoom、Notion、Slack 等產(chǎn)品。
AfterShip Tracking 屬于國(guó)際 SaaS 中的 B2B2C 產(chǎn)品,商家訂閱 AfterShip 的服務(wù),提供訂單信息給我們,我們?yōu)樯碳姨峁┌粉櫡?wù),商家可以從我們這獲取到物流更新反饋給他們的消費(fèi)者,也可以通過(guò)我們的一體化服務(wù)為他們的消費(fèi)者提供通知等服務(wù)。

SaaS 2B 和 2C 的差異
-
產(chǎn)品定位 - 2C 產(chǎn)品通常滿足個(gè)人消費(fèi)者的便利和娛樂(lè)等日常需求;2B 產(chǎn)品主要是支持企業(yè)關(guān)鍵業(yè)務(wù)流程和運(yùn)營(yíng)的重要工具。
-
穩(wěn)定性影響 - 如果 2C 服務(wù)不穩(wěn)定,會(huì)對(duì)個(gè)人消費(fèi)者的使用體驗(yàn)造成影響,但不一定會(huì)對(duì)企業(yè)造成損失;如果 2B 服務(wù)不穩(wěn)定,可能會(huì)導(dǎo)致業(yè)務(wù)中斷影響到企業(yè)的正常運(yùn)營(yíng),造成企業(yè)嚴(yán)重的損失。
-
穩(wěn)定性承諾 - 2C 業(yè)務(wù)一般企業(yè)不會(huì)對(duì)個(gè)人消費(fèi)者有太多穩(wěn)定性的承諾;2B 業(yè)務(wù)在合同中包含 SLA 條款則很常見(jiàn)。 SLA 用于規(guī)定服務(wù)提供商與客戶之間的服務(wù)水平標(biāo)準(zhǔn)和責(zé)任;通常包含了服務(wù)的可用性、響應(yīng)時(shí)間、故障處理和解決時(shí)間等關(guān)鍵指標(biāo),以確保服務(wù)的質(zhì)量和可靠性。
國(guó)內(nèi) SaaS 和國(guó)際 SaaS 的技術(shù)規(guī)劃的差異
-
專(zhuān)注和極致 - 國(guó)際化 SaaS 產(chǎn)品在細(xì)節(jié)打磨上會(huì)有更多的投入,包括對(duì)于產(chǎn)品的交付質(zhì)量有更高的要求,尤其是產(chǎn)品還在早期時(shí)候也普遍有較高的質(zhì)量和標(biāo)準(zhǔn)化。
-
規(guī)范和標(biāo)準(zhǔn) - 體現(xiàn)在如 API 的定義,普遍都是遵循 RESTful API 標(biāo)準(zhǔn),并且都有相對(duì)規(guī)范的 API 設(shè)計(jì)和定義。
-
安全合規(guī) - 在全球范圍內(nèi),尤其是歐盟、美國(guó)和中國(guó)在安全隱私要求上都日趨嚴(yán)格,也都有出臺(tái)了對(duì)應(yīng)的條例:GDPR、CCPA 和《個(gè)人信息保護(hù)法》。另外對(duì)于一家 SaaS 公司來(lái)說(shuō),當(dāng)所服務(wù)的客戶群體有更多是大客戶時(shí),對(duì)于安全隱私方面的要求也會(huì)更高。
-
全球化 - 設(shè)計(jì)全球化的系統(tǒng),相比面向單一國(guó)家/地區(qū)的系統(tǒng),會(huì)需要關(guān)注更多的維度。在做系統(tǒng)設(shè)計(jì)時(shí),系統(tǒng)應(yīng)該預(yù)先設(shè)計(jì)為支持多國(guó)家/地區(qū)、多語(yǔ)言、多時(shí)區(qū)和多幣種等。
1.2 API 的重要性
API 也是產(chǎn)品的一部分,而不僅僅是技術(shù)。對(duì)于 SaaS 產(chǎn)品,提供 API 服務(wù)幾乎是與企業(yè)級(jí)用戶合作的必需項(xiàng),特別是在海外。
-
自動(dòng)化 - 通過(guò) API,企業(yè)可以利用 SaaS 產(chǎn)品的功能來(lái)構(gòu)建自動(dòng)化流程和工作流程。
-
定制化 - 通過(guò) API,企業(yè)可以開(kāi)發(fā)自己的應(yīng)用程序、插件或集成其他工具,以滿足特定的業(yè)務(wù)需求。
-
數(shù)據(jù)集成 - 通過(guò) API,企業(yè)可以靈活地與 SaaS 提供商實(shí)現(xiàn)數(shù)據(jù)的共享和交換。
1.2.1 標(biāo)準(zhǔn)與規(guī)范
AfterShip 的 API 嚴(yán)格循序標(biāo)準(zhǔn)與規(guī)范,做到極致的細(xì)致和嚴(yán)謹(jǐn),主要包括 RESTful、全球化的架構(gòu)設(shè)計(jì)和安全性。



1.2.2 AfterShip API 的演進(jìn)路線
AfterShip 從十萬(wàn)級(jí)發(fā)展到到十億級(jí)的包裹追蹤,從沒(méi)有 API 到高標(biāo)準(zhǔn)的 API,不斷拔高標(biāo)準(zhǔn)的 API 設(shè)計(jì),有力地支撐了業(yè)務(wù)的增長(zhǎng)。


1.3 沒(méi)有 API Versioned 時(shí)面臨的挑戰(zhàn)
挑戰(zhàn)一、變更不規(guī)律,打亂客戶更新計(jì)劃
隨著 AfterShip 的客戶越來(lái)越多、品牌越來(lái)越大,要求越來(lái)越嚴(yán)格,對(duì)我們的挑戰(zhàn)越來(lái)越大。有些客戶反饋,原來(lái)一個(gè)月的通知時(shí)間太短了,你們應(yīng)該提前三個(gè)月通知,更有甚者表示沒(méi)有計(jì)劃配合我們對(duì)已有的 API 進(jìn)行任何變更。
挑戰(zhàn)二、新功能發(fā)布缺少調(diào)試環(huán)境
研發(fā)們都知道,在我們對(duì)接新的接口協(xié)議時(shí),除了對(duì)接接口文檔一般都有一個(gè)測(cè)試環(huán)境聯(lián)調(diào)的過(guò)程。一方面我們要求客戶配合變更,雖然我們有提供對(duì)接文檔,但我們線上只有一個(gè)版本的 API,缺乏“測(cè)試環(huán)境”,用戶根本沒(méi)有辦法調(diào)試。

2. What
為了解決上述的問(wèn)題,我們開(kāi)始學(xué)習(xí)行業(yè)內(nèi)的知名公司是怎么做的。像 Github, Stripe, Shopify 和 Microsoft 這些公司的 API,都有 API Versioning 的概念。
2.1 本質(zhì)
很多同學(xué)可能以為 URL 上帶上 v1, v2, v3 這樣的版本號(hào)字眼就是 versioning,其實(shí)不是。從 2014 年開(kāi)始我們的 API 就帶上了 v4 的版本號(hào),但一直都沒(méi)有實(shí)現(xiàn) Versioning。
API Versioning 的本質(zhì)在于

-
API 有版 本概念。
-
同一個(gè) 版本的每次 API 變 更都能向后兼容, 向后兼容 - API 的穩(wěn)定性要求避免破壞性的更改。在進(jìn)行更新或修改時(shí),應(yīng)保持對(duì)現(xiàn)有功能的支持,以防止影響已部署的應(yīng)用程序或客戶端。
-
不能向后兼容的發(fā)布只會(huì)發(fā)生在新版本中。
怎么評(píng)判一個(gè) API 能不能做到向后兼容呢?
我們來(lái)看看 Github 關(guān)于 API Versioning 的介紹。
所有做 API Versioning 的企業(yè),都會(huì)對(duì) breaking changes 有一套自己的規(guī)范。不同企業(yè)會(huì)因?yàn)闃I(yè)務(wù)形態(tài)不同因此對(duì) breaking changes 有不同的定義,有些企業(yè)可能比較嚴(yán)格,有些企業(yè)可能比較寬松,但大原則是如果變更可能影響到客戶端已有的集成即認(rèn)為該變更是 breaking changes。
只有做到對(duì) API 不發(fā)布任何 breaking changes 才能叫向后兼容。

2.2 實(shí)現(xiàn)效果
一個(gè)非 Versioned 的 API 就像我們剛才提到的,所有功能都在一個(gè)版本上迭代,就會(huì)遇到我剛才提到的問(wèn)題。
一個(gè) Versioned 的 API,將提供多套穩(wěn)定的 API 在線上,每套 API 有獨(dú)立的邏輯、實(shí)例、資源,各版本之間互不影響。客戶端可以自由選擇調(diào)用哪個(gè)版本。

舉個(gè)例子,對(duì)于剛才提到的問(wèn)題,如果用戶在使用 2023-10 這個(gè)版本,此時(shí)我們需要發(fā)布新功能,我們新發(fā)布的內(nèi)容只會(huì)在 2024-01 的版本發(fā)布,完全不會(huì)影響到 20 23-10 這個(gè)版本。
當(dāng)新版本發(fā)布后,用戶在生產(chǎn)環(huán)境上可以繼續(xù)使用 2023-10 的版本,同時(shí)他們把 2024-01 這個(gè)版本當(dāng)作他們的“測(cè)試環(huán)境” 進(jìn)行聯(lián)調(diào),實(shí)現(xiàn)對(duì)他們的業(yè)務(wù)零 downtime 升級(jí)。
3. How
3.1 Support Policy
通過(guò)技術(shù)調(diào)研,我們發(fā)現(xiàn)企業(yè)新增一個(gè)版本都會(huì)帶來(lái)額外的維護(hù)成本,所以版本發(fā)布頻率高的企業(yè),版本支持時(shí)間較 短; 版本發(fā)布 頻率低的企業(yè),版本支持時(shí)間較長(zhǎng)。 企業(yè)可以根據(jù)自身的業(yè)務(wù)特性選擇合適的 Support Policy。
AfterShip 的 Support Policy :
1、版本發(fā)布周期: 三個(gè)月定期發(fā)布
2、版本支持時(shí)長(zhǎng):十八個(gè)月

3.2 API version
版本指定方式如圖所示:

業(yè)界主 要有通過(guò) request header 和 URL path 兩種指定 API version。 我們考慮通過(guò) URL 這種方式更清晰更好維護(hù)所有采用這種方式。
3.3 Webhook version
指定版本: 對(duì)于 Webh ook 的版本選擇有些不同,Webhook URL 由用戶提供,在 URL 中指定版本有些不現(xiàn)實(shí)。 業(yè)界的主要方式是當(dāng)用戶在配置 Webhook URL 的時(shí)候需要指定一個(gè)版本,后續(xù) webhook 將以指定的版本向該 URL 發(fā)送。
確認(rèn)版本: 通過(guò)接收到的 webhook request header 確認(rèn),header as-webhook-version 即表示該 webhook 的版本。
3.4 公開(kāi)文檔
文檔頁(yè)面包含所有 live 的版本
Migration guide: 每個(gè)版本有對(duì)應(yīng)的指引引導(dǎo)用戶如何從上一個(gè)版本升級(jí)到當(dāng)前版本。
3.5 系統(tǒng)架構(gòu)
3.5.1 Overview
API 請(qǐng)求流程 用戶的 API 請(qǐng)求首先會(huì)經(jīng)過(guò)我們的外部網(wǎng)關(guān)身份、權(quán)限、Rate limit 等驗(yàn)證,然后根據(jù)不同的 URL 把請(qǐng)求分發(fā)到不同的 API 版本應(yīng)用。應(yīng)用層把請(qǐng)求處理后轉(zhuǎn)發(fā)到能力層,能力層與數(shù)據(jù)庫(kù)、消息隊(duì)列等組件進(jìn)行交互完成請(qǐng)求。 Webhook 推送流程 當(dāng)數(shù)據(jù)發(fā)生更新時(shí),有一個(gè)消息分發(fā)器負(fù)責(zé)監(jiān)聽(tīng)數(shù)據(jù)總線。根據(jù)數(shù)據(jù)的用戶歸屬找到對(duì)應(yīng)的 Webhook 配置,根據(jù) Webhook 配置上的版本信息把消息分發(fā)對(duì)應(yīng) webhook 版本的應(yīng)用進(jìn)行處理。
3.5.2 Trade off
External API 這一層屬于應(yīng)用層。 運(yùn)行的是對(duì)外的 API,每個(gè)應(yīng)用對(duì)應(yīng)一個(gè) API 版本,這種設(shè)計(jì)是為了讓每個(gè)版本的運(yùn)行環(huán)境隔離,發(fā)布新版本時(shí)肯定不會(huì)影響其它版本。每個(gè)版本有其特有的 Versioning 邏輯,如對(duì)應(yīng)版本的 URL 和 header,也有其特有的版本業(yè)務(wù)邏輯。 Internal API 這一層屬于能力層。 運(yùn)行的是領(lǐng)域 API,每個(gè)應(yīng)用大多對(duì)應(yīng)一個(gè)領(lǐng)域,為上層應(yīng)用提供能力。在這一層沒(méi)有 API 版本的概念,每個(gè) API 依賴一個(gè)數(shù)據(jù)庫(kù)。為什么?成本問(wèn)題。假設(shè)最簡(jiǎn)單的情況:有 5 個(gè)領(lǐng)域 API,每個(gè)支持 6 個(gè)版本,就需要維護(hù) 30 個(gè)應(yīng)用;但實(shí)際上我們的系統(tǒng)架構(gòu)還要遠(yuǎn)比這復(fù)雜,每個(gè) API 還將依賴其它更底層的 API、消息隊(duì)列、數(shù)據(jù)庫(kù)等資源。如果要完全做到版本化,這里需要維護(hù)的版本依賴將是個(gè)很可怕的數(shù)字。作為 trade off,我們只在應(yīng)用層做版本化,每個(gè)版本維護(hù)的業(yè)務(wù)邏輯實(shí)際上是對(duì)能力層的兼容。
3.6 版本維護(hù)
代碼: 不同 API 版本的代碼維護(hù)在同一個(gè)代碼倉(cāng)庫(kù),但每個(gè) API 版本有一個(gè)獨(dú)立的代碼分支。 基于代碼分支構(gòu)建鏡像,以不同鏡像發(fā)布不同版本的應(yīng)用。 自動(dòng)化測(cè)試: 為每個(gè) API 版本設(shè)計(jì)其獨(dú)立的測(cè)試用例,在每次發(fā)布前檢查每個(gè)版本是否存在行為變更。 監(jiān)控看板: 根據(jù)每個(gè)版本的流量特性添加獨(dú)立的監(jiān)控規(guī)則,如果存在某個(gè)版本存在比較特別的業(yè)務(wù)邏輯也可能要增加特定的業(yè)務(wù)指標(biāo)。
4. Takeaway 我們總結(jié)了一些經(jīng)驗(yàn)供各位直接借鑒:
-
API 不僅僅是技術(shù),也是一款產(chǎn)品。如果想要和企業(yè)用戶合作,API 是必需項(xiàng)。在一開(kāi)始就應(yīng)該以高標(biāo)準(zhǔn)、高要求規(guī)劃我們的 API。
-
API Versioning 的本質(zhì)是最大化解決向后兼容問(wèn)題,已存在的 API 不會(huì)出現(xiàn)向后不兼容的變更,不能向后兼容的功能只會(huì)在新版本中發(fā)布。
-
根據(jù)我們業(yè)務(wù)迭代的速度,決定是按固定周期發(fā)布版本還是按需發(fā)布新版本。比如我們平均每三個(gè)月發(fā)布一個(gè)新功能,可以考慮以 3 個(gè)月為周期發(fā)布版本,每個(gè)版本保留 12 個(gè)月,那我們同時(shí)最多只需要維護(hù) 4 個(gè)版本的 API。
-
建議在應(yīng)用層實(shí)現(xiàn) Versioning,不建議在能力層實(shí)現(xiàn)。雖然可以做到非常極致,但是業(yè)務(wù)復(fù)雜后維護(hù)成本也是相當(dāng)可怕。
推薦閱讀:
每秒百萬(wàn)數(shù)據(jù)點(diǎn) Go 應(yīng)用監(jiān)控系統(tǒng)演進(jìn)
「GoCN酷Go推薦」我用go寫(xiě)了魔獸世界登錄器?
Go區(qū)不大,創(chuàng)造神話,科目三殺進(jìn)來(lái)了
想要了解Go更多內(nèi)容,歡迎掃描下方??關(guān)注公眾號(hào), 回復(fù)關(guān)鍵詞 [實(shí)戰(zhàn)群] ,就有機(jī)會(huì)進(jìn)群和我們進(jìn)行交流
分享、在看與點(diǎn)贊Go
