• <strike id="fdgpu"><input id="fdgpu"></input></strike>
    <label id="fdgpu"></label>
    <s id="fdgpu"><code id="fdgpu"></code></s>

  • <label id="fdgpu"></label>
  • <span id="fdgpu"><u id="fdgpu"></u></span>

    <s id="fdgpu"><sub id="fdgpu"></sub></s>
    您當前的位置是:  首頁(yè) > 資訊 > 國內 >
     首頁(yè) > 資訊 > 國內 >

    【融云分析】如何保障 API 設計的穩定性(內含福利)

    2019-06-24 11:13:41   作者:   來(lái)源:CTI論壇   評論:0  點(diǎn)擊:


      計算機行業(yè)有句名言 —— 計算機科學(xué)領(lǐng)域的任何問(wèn)題,都可以通過(guò)增加一個(gè)間接的中間層來(lái)解決。
      當前的計算機領(lǐng)域,無(wú)論廣度還是深度,已經(jīng)沒(méi)有一個(gè)人能完全掌握了。但是,通過(guò)各種中間層的組合使用,我們不需要了解其內部細節,也可以像搭積木一樣,開(kāi)發(fā)出各種有趣的服務(wù)和應用。
      而各個(gè)中間層之所以能組合工作,正是因為大家都通過(guò)定義好的 API 交互和通信。每個(gè)模塊在對外提供經(jīng)過(guò)抽象 API 的同時(shí),也需要使用其他模塊的 API 作為自身運行的基礎。
      今天我們來(lái)聊聊融云在設計 API 過(guò)程保障穩定性的一些實(shí)踐。
        無(wú)處不在的 API
      API(Application Programming Interface) 又稱(chēng)為應用編程接口。
      而接口,本質(zhì)可以理解為契約,一種約定。
      計算機接口的概念起源于硬件。早期各家研發(fā)的各種元器件都不通用也沒(méi)有標準,相互使用非常困難,于是大家約定了功能和規格,就產(chǎn)生了接口,后來(lái)蔓延到軟件中。
      接口蔓延到軟件之后,又分為ABI(Application Binary Interface)和API(Application Programming Interface) 。
      前者主要約定了二進(jìn)制的運行和訪(fǎng)問(wèn)的規則,后者則專(zhuān)注于邏輯模塊的交互。本文以下內容僅討論開(kāi)發(fā)者經(jīng)常接觸的 API。
      很多人對 API 的印象只是包含一些函數的 Class 或 頭文件。但 API 在我們生活中無(wú)處不在,只是我們有時(shí)并沒(méi)有注意到。
      比如,當我們在撥打電話(huà)時(shí),手機和基站通信的整個(gè)系統是非常復雜的。
      好在我們不需要了解內部的細節,僅需要把 11 位的電話(huà)號碼傳給“電話(huà)系統”的接口就可以,而隱藏的國家區號(如+86)可以理解為接口的默認參數。
      這個(gè)高度抽象的 API 背后,隱藏了非常多的細節。借助上面的中間層理論,我們可以系統性地討論設計一個(gè) API 所需要考慮哪些內容。
      1. 模塊對上層暴露的 API 如何被使用?
      API 從使用的耦合方式上,可以分為兩類(lèi):一種是通過(guò)協(xié)議調用,如調用 HTTP 接口;另一種是語(yǔ)言直接通過(guò)聲明調用。
      如設計 HTTP Restful API 時(shí),并不需要關(guān)心使用者的操作系統、使用的編程語(yǔ)言、內存線(xiàn)程管理等,因此會(huì )比后者簡(jiǎn)單一些。
      API 從使用者的規模和可控范圍上,可以分為 LSUD(Larget Set of Unkown Developers) 和 SSKD(Small Set of Kown Developers) 兩種。
      前者一般都是公網(wǎng)開(kāi)放的云服務(wù),任何開(kāi)發(fā)者都可以使用,無(wú)法提前預知以何種姿勢被使用,版本也不可控制。融云提供的通信云就是這種 API。
      后者用戶(hù)群有限,一般都在同一家公司或團隊內。比如前段時(shí)間比較火的組件化,即對內提供的模塊化 API,使用范圍和方式均可控,在更新時(shí)一般不用太糾結向后兼容。
      API 的第一受眾是人,然后才是機器,所以“可理解性”在設計時(shí)需要優(yōu)先考慮。
      而良好的 API 文檔、簡(jiǎn)單扼要的 Demo、關(guān)鍵的 log,可以提升 API 使用者的體驗。
      2. API 所屬模塊對下層有什么依賴(lài)?
      API 所屬模塊都運行在一定的地址空間中。而其中的環(huán)境變量、加載庫、內存和線(xiàn)程模型、系統和語(yǔ)言特性都需要考慮。
      3. API 所屬模塊的內部實(shí)現對其他層有什么影響?
      一般而言,設計良好的 API 在使用時(shí),并不需要理解其內部實(shí)現。但如果能了解其內部架構并輔助關(guān)鍵 log,有助于提升使用 API 的效率。
      并且模塊的內部實(shí)現,有時(shí)也會(huì )影響到 API 設計的風(fēng)格。
      如一個(gè)強依賴(lài) IO 的接口,可能需要使用異步的方式。大量異步的方式,就衍生出了 RxJava 等框架。
      向后兼容
      因為 API 如此重要,涉及的范圍又如此廣泛,廣大開(kāi)發(fā)者對 API 的向后兼容可以說(shuō)要求非常高。
      畢竟誰(shuí)也不想在開(kāi)發(fā)過(guò)程中,頻繁的更新接口和代碼,想想《 swift 從入門(mén)到精通到再次入門(mén)到再再次入門(mén)》的慘案就心有余悸。
      我們不僅問(wèn),為什么很多公司或者項目都無(wú)法向后兼容,僅僅是投入不夠或不夠重視,還是說(shuō) 100% 的向后兼容實(shí)際就是不可能的?
      假設設計是理想和經(jīng)過(guò)論證的,正如一個(gè)完美的圓圈。
      設計是要落實(shí)到編碼中的,而編碼的過(guò)程中總是不可避免的引入一些 bug,而帶著(zhù) bug 的某個(gè)版本實(shí)現,其實(shí)正如一個(gè) Amoeba 變形蟲(chóng),形態(tài)是不固定的。而隨著(zhù)版本不斷演進(jìn),不可避免會(huì )產(chǎn)生一定的差異。
      第一個(gè)版本實(shí)現:
      第二個(gè)版本實(shí)現:
      所以說(shuō) 100% 向后兼容本身就是不可能的。
      因此,大家平時(shí)在談?wù)?API 穩定性時(shí),其實(shí)默認是可以包含一定程度變更的。
      但由于 API 涉及的范圍太廣泛,保障向后兼容都需要極大代價(jià)。
      比如 Linux 就希望快速迭代,完全不保證 API 的穩定性。針對這個(gè)問(wèn)題,Linux 還特意寫(xiě)了 stable-api-nonsense 文檔。有興趣的可以點(diǎn)擊閱讀:
      stable-api-nonsense.rst
      漸進(jìn)式改進(jìn)
      所以說(shuō),保障 API 的穩定性會(huì )面臨很多挑戰,比如:
    • 業(yè)務(wù)形態(tài)還不穩定,還在高速發(fā)展
    • 業(yè)務(wù)和 API 歷史包袱較重
    • 多個(gè)平臺和語(yǔ)言的特性不一致
    • 用戶(hù)群和使用方式不明確
      我們回顧一下正常的開(kāi)發(fā)流程,看看是否能通過(guò)一些指標和工具,改善 API 的穩定性,主要涉及:需求、設計、編碼、Review、測試、發(fā)布、反饋等步驟。
      需求
      普通的產(chǎn)品開(kāi)發(fā),在啟動(dòng)的時(shí)候,用戶(hù)需求都比較明確,但對于 LSUD 的云服務(wù)而言,無(wú)法提前預知用戶(hù)群都有哪些,以及用戶(hù)在他的產(chǎn)品中如何使用 API。
      這容易造成,沒(méi)有明確的用戶(hù)需求,API 就不好進(jìn)行設計和迭代,沒(méi)有設計就沒(méi)有用戶(hù),需求更無(wú)從談起。這是一個(gè)雞生蛋、蛋生雞的問(wèn)題。
      建議可以在 API 發(fā)布之前,內部先針對典型的使用場(chǎng)景,設計幾個(gè)完整的 Demo,驗證 API 的設計和使用是否合理。
      需要注意的是,Demo 需要有完整應用場(chǎng)景,達到上架地步,如果能內部使用, Eating your own dog food 最好,過(guò)于簡(jiǎn)單的 Demo 無(wú)法提前暴露 API 的使用問(wèn)題。
      Demo 的開(kāi)發(fā)人員最好與 API 的設計者有所區分,避免思維固化,更多內容大家可以參照 Rust 語(yǔ)言開(kāi)發(fā)在自舉過(guò)程中的一些實(shí)踐。
      設計
      在設計 API 的時(shí)候,有很多需要注意的點(diǎn)和普通開(kāi)發(fā)不太一樣。
      普通開(kāi)發(fā),快速實(shí)現功能始終被放在第一位。比如大家會(huì )用一些敏捷開(kāi)發(fā)的方式,優(yōu)先實(shí)現功能再快速迭代等。
      但 API 設計時(shí),接口無(wú)法頻繁變更,所以首先需要考慮的是“少”,少即是多。
      每個(gè) API 做的事情要少
      一個(gè)接口只做一件事,把這個(gè)事情做好就足夠了。
      需要避免為了討好某個(gè)場(chǎng)景,在一個(gè) API 上進(jìn)行復雜的組合邏輯,提供一個(gè)類(lèi)似語(yǔ)法糖的接口。否則,場(chǎng)景的業(yè)務(wù)自身在演進(jìn)時(shí),很難保證 API 的行為不變。
      如果需要支持多種業(yè)務(wù),可以考慮將 API 分層,比如融云客戶(hù)端的 API 會(huì )分為下面幾層。
      舉個(gè)例子,融云考慮通用性,基于訂閱分發(fā)的模型,抽象了 RTCLib,客戶(hù)端能處理媒體的任意流,非常的靈活,但是對于用戶(hù)而言開(kāi)發(fā)代價(jià)可能高些,要思考和做的工作比較多。
      考慮到大量的用戶(hù),其實(shí)需要的是音視頻通話(huà)的業(yè)務(wù),基于 RTCLib,融云分裝了不帶 UI 的 CallLib 以及集成了 UI 的 CallKit。
      如果一個(gè)用戶(hù),需求和微信的音視頻通話(huà)類(lèi)似,可以集成帶 UI 界面的 CallKit,開(kāi)發(fā)效率會(huì )非常高;
      如果用戶(hù)對通話(huà)音視頻通話(huà) UI 的交互有大量需求,可以基于 CallLib 進(jìn)行開(kāi)發(fā),對 UI 可以進(jìn)行各種定制。
      暴露的信息要少 
      成熟的 API 設計者都會(huì )盡可能的隱藏內部實(shí)現細節。
      比如字段不應該直接暴露而是通過(guò) Getter/Setter 提供,不需要的類(lèi)、方法、字段都應該隱藏,都已經(jīng)成為各個(gè)語(yǔ)言的基礎要求,在此就不細述了。
      但容易被忽略的一點(diǎn)需要提醒大家,應盡量隱藏技術(shù)棧的信息。
      比如:API http://api.example.com/cgi-bin/
      get_user.php?user=100,就明顯混入了很多無(wú)用的信息,并且以后技術(shù)切換升級想維持 API 穩定非常麻煩。
         行為擴散要少
      在語(yǔ)言直接調用的 API 中,需要避免基礎接口通過(guò)繼承導致行為擴散。
      在普通的編碼過(guò)程中,抽象類(lèi)和繼承都是面向對象的強大武器。但是對于 API,更建議通過(guò)組合使用。
      比如一個(gè)管理生命周期的類(lèi),如果被繼承,子類(lèi)有些行為就有可能被修改而導致出錯。這時(shí)候建議使用 Interface + 工廠(chǎng)的方法提供實(shí)例。
      由于 Java 8 之前 interface 沒(méi)有 default 實(shí)現,為了避免增加功能需要頻繁修改接口,可以使用 final class。
      Objetive-C則可以使用 __attribute__ ((objc_subclassing_restricted))和__attribute__((objc_requires_super))控制子類(lèi)繼承行為。
      畫(huà)風(fēng)切換要少
      API 命名要做到多個(gè)平臺的業(yè)務(wù)命名統一,與每個(gè)平臺的風(fēng)格統一。
      這點(diǎn) HTTP 的接口要簡(jiǎn)單一些,只需要選定一種風(fēng)格即可,Restful 或者 GraphQL 或者自己定義。
      語(yǔ)言調用的 API 命名,建議首先遵循平臺的風(fēng)格,然后再是參考語(yǔ)言標準,最后才考慮團隊的風(fēng)格。
      比如:iOS 平臺的 API 開(kāi)發(fā),需要首先參照 iOS 的命名風(fēng)格,did 和 will 之類(lèi)的時(shí)態(tài)就非常有特色。
      命名上細節較多,詞匯、時(shí)態(tài)、單復數、介詞、?小寫(xiě)、同步異步風(fēng)格等都需要考量,需要長(cháng)時(shí)間的積累。
      理解成本要少
      一般 API 每個(gè)接口都會(huì )有相應的注釋說(shuō)明,但是值得注意的是,大部分開(kāi)發(fā)者并不看注釋。
      大部分開(kāi)發(fā)者對接口的了解,都僅源于 IDE 的補全和提醒。一個(gè)接口看著(zhù)像就直接用,不行再換一個(gè)試試,這其實(shí)是一種經(jīng)驗式編程的方式。也就意味著(zhù)接口命名需要提高可理解性。有一個(gè)辦法可以驗證,將接口的所有注釋抹掉,使用者能否非常直接的看懂每個(gè)接口的含義。如果很困難,則需要改進(jìn)。
      API 設計還有一處和普通開(kāi)發(fā)不太一致。普通開(kāi)發(fā)設計好架構即可,每個(gè)模塊的開(kāi)發(fā)可能是同一個(gè)人,接口并不需要在設計時(shí)確定下來(lái)。但是 API 的設計階段,需要進(jìn)行 Review 并直接確定接口的設計,以保證多端在開(kāi)發(fā)時(shí)遵循完全一直的規則。
      編碼
      在 API 的編碼過(guò)程中,有以下幾點(diǎn)需要注意。
      1、在 API 中,預定義好版本號。
      這個(gè)主要是針對 HTTP API,如:
      http://api.example.com/v1/users/12345?fields=name,age。
      如果目前僅有一個(gè)版本,也可以暫時(shí)不加,第二版時(shí)再區分。
      2、注意 API 版本檢查。
      當分層提供多種 API 時(shí),每層 API 需要在啟動(dòng)時(shí),先校驗一下版本號,避免不匹配的情況。
      比如在以下 Java 代碼中,大家可能覺(jué)得判斷版本號相等的代碼非常奇怪,應該永遠是 true 才對。
      但是抽象類(lèi)和實(shí)現類(lèi)出現在不同的分層模塊中,并且實(shí)現類(lèi)先編譯,抽象類(lèi)版本更新后再編譯,就會(huì )出現不一致的情況。有很多語(yǔ)言或平臺能提供類(lèi)似的方式來(lái)確定版本。
      3、提供規范性的 log 輸出。
      普通開(kāi)發(fā)的log,主要用于自己定位問(wèn)題。但是 API 在編碼時(shí),最好針對性的添加一些 log,有利于 API 的使用者理解并簡(jiǎn)單排查問(wèn)題。但出于性能考慮,需要定義好 log 的級別并可以調整。
      4、注意廢棄與遷移。
      當一個(gè)以前設計的 API 不再符合要求或者有重大問(wèn)題時(shí),我們可以對外標記成已廢棄,并在注釋中建議使用者遷移到另一個(gè)接口。如果是類(lèi)似的被廢棄接口,內部編碼時(shí)最好能使用新的接口來(lái)實(shí)現,以降低向后兼容的維護成本。HTTP 的 API,需要預定義好遷移的錯誤碼,比如在 HTTP 規范中,可以使用 410 Gone 說(shuō)明已經(jīng)不再支持某個(gè)接口。
      Review
      API 的 Review 基于普通開(kāi)發(fā)的 Code Review。如果基礎的 Code Review 都沒(méi)有做好,肯定無(wú)法保障 API 的質(zhì)量和穩定性。
      可以通過(guò)一些工具,為 API 的 Review 提供一些參考報告。比如可以使用 SonarLint 分析代碼復雜度,如果接口層的代碼復雜度較高,會(huì )是一個(gè)危險的信號。還可以借助 Java 反射、Clang 語(yǔ)法分析,獲取當前的 API 接口列表,生成接口變更報告,也有利于減少無(wú)用接口的暴露。另外,自動(dòng)化工具生成的接口文檔也是 Review 重要的一環(huán)。
      測試
      在測試環(huán)節,我們可以通過(guò) unit test 來(lái)關(guān)注 API 的穩定性。與敏捷開(kāi)發(fā)經(jīng)常修改 test case 不同,API 的 test case 基本代表了接口的穩定性。所以在修改舊 case 時(shí)需要特別明確,是 case 自身的 bug 還是接口行為發(fā)生了變更。
      發(fā)布
      我們可以通過(guò)區分 dev 和 stable 版本,為不同階段的開(kāi)發(fā)者提供更好的體驗。
      dev 版本包含最新的功能,但是 API 接口有變更風(fēng)險。stable 版本 API 穩定,但功能不一定是最新的。如果開(kāi)發(fā)者還在開(kāi)發(fā)過(guò)程中,可以選用最新的 dev 版本,基于最新 API 開(kāi)發(fā)。如果應用已經(jīng)上線(xiàn),可以選擇升級直接到最新的 stable 版本。
      反饋
      由于前面提到的,云服務(wù)的 API 比較難確定用戶(hù)群和用戶(hù)的使用方式。可以參考 APM(Application Performance Management) 的方式,記錄熱點(diǎn) API 使用情況,為后續的優(yōu)化提供數據。
      總結
      上面的改進(jìn),讓保障 API 的穩定性變得更容易。下面以融云 IMLib iOS SDK 2.0 版本演進(jìn)為例,歷盡 2015至 2019 四年時(shí)間,從 2.2.5 到 2.9.16 共 98 個(gè)版本。API 接口數量翻了一番,考慮到接口更內聚,功能大約增加了 3 倍。
      但是需要用戶(hù)遷移的接口非常少,即使遷移時(shí)開(kāi)發(fā)成本都非常低。


      看完整篇文章大家是否get到了保障 API 設計的穩定性方法
      下面小編來(lái)考考大家,答題還有禮品拿哦
      參與方式:
    • 在文末下方留言區寫(xiě)下你的答案
    •  獲得點(diǎn)贊排名前5的小伙伴,可獲得定制禮包(定制T恤和8G U盤(pán))
    • 點(diǎn)贊沒(méi)那么高也不要灰心,小編會(huì )在精選留言中抽出5個(gè)小伙伴送出單項禮品(定制T恤)
      活動(dòng)時(shí)間:
      即刻起至 6 月 26 日 9:30
      中獎公布:
      中獎結果將于下周推文公布
      快來(lái)答題吧:
      ① API 是不是可以做到 100% 的向后兼容?
      A.  是
      B.  不是
      ② HTTP API 如果不幸發(fā)生永久遷移,可以使用什么 HTTP 錯誤碼?
      A.  302 Moved Temporarily
      B.  404 Not Found
      C.  410 Gone
      ③ 開(kāi)發(fā)者還在開(kāi)發(fā)過(guò)程中,可以選用融云的最新___版本,基于最新 API 開(kāi)發(fā)。
      A.  dev 版本
      B.  stable 版本
      C.  beta 版本
    【免責聲明】本文僅代表作者本人觀(guān)點(diǎn),與CTI論壇無(wú)關(guān)。CTI論壇對文中陳述、觀(guān)點(diǎn)判斷保持中立,不對所包含內容的準確性、可靠性或完整性提供任何明示或暗示的保證。請讀者僅作參考,并請自行承擔全部責任。

    專(zhuān)題

    CTI論壇會(huì )員企業(yè)

    亚洲精品网站在线观看不卡无广告,国产a不卡片精品免费观看,欧美亚洲一区二区三区在线,国产一区二区三区日韩 慈溪市| 德江县| 霍山县| 佛山市| 桦川县| 荥经县| 武川县| 盖州市| 沙雅县| 孝义市| 香港| 连山| 额敏县| 塘沽区| 河东区| 通辽市| 西安市| 凤凰县| 西华县| 朝阳市| 扶风县| 资兴市| 临邑县| 花莲县| 资溪县| 武山县| 长岛县| 亚东县| 徐汇区| 平远县| 麻栗坡县| 黑山县| 汾西县| 常熟市| 左权县| 武乡县| 安图县| 库伦旗| 奈曼旗| 石林| 博客| http://444 http://444 http://444 http://444 http://444 http://444