1. 程式人生 > >高可用、高效能? 介面設計的 16 個原則

高可用、高效能? 介面設計的 16 個原則

640.gif?wxfrom=5&wx_lazy=1

本文來自作者LY 在 GitChat 上分享「如何設計出高可用、高效能的介面」,閱讀原文」檢視交流實錄

文末高能

編輯 | 嘉仔

發起這個 Chat 只是一時興起,想了一些點就寫出來了,但自己一讀,感覺一點乾貨都沒有,真是汗顏。但還是也希望此拙文能帶來一些你的思考,歡迎交流。

介面設計需要考慮哪些方面

  1. 介面的命名。

  2. 請求引數。

  3. 支援的協議。

  4. TPS、併發數、響應時長。

  5. 資料儲存。DB選型、快取選型。

  6. 是否需要依賴於第三方。

  7. 介面是否拆分。

  8. 介面是否需要冪等。

  9. 防刷。

  10. 介面限流、降級。

  11. 負載均衡器支援。

  12. 如何部署。

  13. 是否需要服務治理。

  14. 是否存在單點。

  15. 介面是否資源包、預載入還是內建。

  16. 是否需要本地快取。

  17. 是否需要分散式快取、快取穿透怎麼辦。

  18. 是否需要白名單。

當我們設計介面,我們或多或少都會有上面列舉的一些考慮,我們只有想的更多才能讓讓我們的介面更加完善,我個人覺得100%完美的介面是不存在,只有適合才是最重要。

介面設計原則

原則一:必須符合Restful,統一返回格式,約定業務層錯誤編碼,每個編碼可以攜帶可選的錯誤資訊。

原則二: 命名必須規範、優雅。

原則三:單一性。

單一性是指介面要做的事情應該是一個比較單一的事情,比如登陸介面,登陸完成應該只是返回登陸成功以後一些使用者資訊即可,但很多人為了減少介面互動,返回一大堆額外的資料。

比如有人設計一個使用者列表介面,介面他返回每一條資料都是包含使用者了一大堆跟另外無關的資料,結果一問,原來其他無關的資料是他下一步想要獲取的,想達成資料的懶加

原則四:可擴充套件。

介面擴充套件性,是指設計介面的時候多想想多種情況,多考慮各個方面,其實我覺得單獨將擴充套件性放在這裡也是不妥的,感覺說的跟單一性有點相反的意思,其實這個不是這個意思。

這邊的擴充套件性是指我們的介面充分考慮客戶端,想想他們是如何呼叫的,他要怎樣使用我的程式碼,他會如何擴充套件我的程式碼,不要把過多的工作寫在你的接口裡面,而應該把更多的主動權交給客戶程式設計師。

如獲取不同的列表資料介面,我們不可能將每個列表都寫成一個介面。 還有一點,我這裡特別想指出來的是很多開發人員為了省事(姑且只能這麼理解),將介面設計當成只是 app 頁面展示。

這些人將一個頁面展示就用一個介面實現,而不考慮這些資料是不是屬於不同的模組、是不是屬於不同的展示範疇、結果下次視覺一改,整個介面又得重寫,不能複用。

原則五:必須有文件。

良好的介面設計,離不開清晰的介面文件表述。文件表述一定要足夠詳細

原則六:產品心。

為什麼我說要有產品心?因為我覺得很多人忽略了這一點。我來說一下假如開發一個app,如果一開始連個互動文件給你都沒有的話,你怎麼設計介面?

所以我覺得作為一個服務端後臺開發人員應該要有產品心,特別是對於互動文件應該好好理解,因為這些都會對我們的介面設計有很大的影響。

我在設計介面的時候就很常發現很多互動文件根本就走不通,產品沒有考慮到位,互動文件缺失,這時候作為一個開發要主動推動,完善。

原則七:第三方服務介面資料能快取就快取。

原則八:第三方服務需要做降級。

原則九:建議消除單點。

原則十:介面粒度要小。

原則十一:客戶端能處理的邏輯就不要給服務端處理,減少服務端壓力。

原則十二:資源預載入。

原則十三:不要過度設計。

原則十四:快取儘量不要穿透。

原則十五:介面能快取就快取。

原則十六:思辨大於執行

如何保證介面的高可用、高效能

上面也列舉很多需要考慮和設計的原則,其實還有很多方面,我這邊也不是特別全面。

居於上面列舉的這些考慮點,其實這邊說服務是更恰當,能把上面說的點做好,其實介面也是比較可靠,如何設計以及保證介面的高可用和高效能。可以思考一下以下幾個 point

高效能:如果我們發現這個介面tps和響應時間沒有達到我們的要求怎麼辦。

  • A:資料儲存方面:我們會想資料庫有沒有分庫、分表、有沒有做主從,有沒有讀寫分離、欄位是否有加索引、是否存在慢 sql,資料庫引擎是否選用合適、是不是用了事務;

    其次我們會想到是不是引用了分散式快取、快取 key 大小是否合適,失效時間是否設定合理,會不會大量快取穿透、有沒有引入本地快取。

  • B:業務方面:是否有大量的計算、能否非同步處理。是否需要引入執行緒池或者 MQ 來非同步處理任務。有沒有必要將介面進行垂直拆分和水平拆分、將介面粒度變小。

  • C:其他方面:nginx 層面做快取、加機器、用 ssd,資源放 cdn,多機房部署、資原始檔預載入。

高可用:如何保證服務高可用,需要從幾個維度來實現:

  • A:消除單點,基於高可用第二位。

  • B:能做叢集的全部做叢集。譬如 Redis 叢集、mysql叢集、MongoDB副本集。

  • C:能做讀寫分離的都做讀寫分離。

  • D:異地多機房部署,接入 GSLB

  • E:必須有限流、降級機制。

  • F:監控。高可用的保證,基於第一位。

下圖是從一個基本的請求出發來梳理需要涉及到各個段,以及各個端能做的事情。談談介面服務,但不侷限於介面本身。

0?wx_fmt=png

  1. 客戶端:資源預載入、限制請求、資料上報。我這邊就拿客戶端來舉個例子。介面服務所依賴的資源包或者一些公共配置預載入在本地,減少介面的互動,通過請求配置檔案是否更新,code是否是304等來;

    介面做一些請求限制,比如搶紅包、搶券等,單位時間內N次點選只請求一次等;介面失敗資料上報來;這就是客戶端可以做到的對介面有幫助的事情

  2. GSLB/HttpDNS:多機房部署、流量切換、域名劫持,一般技術和業務比較成熟的公司這一層。

  3. 資原始檔放CDN。

  4. 負載均衡器:lVS+Nginx是網際網路常用的做負載均衡,可以實現四層/七層負載均衡;這裡除了可以分流、轉發以外,我們用的更多的基於令牌桶限流、快取。

  5. 本地快取。本地快取能減少我們訪問DB或者分散式快取,本地快取推薦使用guava,guava裡面有很多特性很好用,例如基於令牌桶的限流;當快取失效時只穿透一個請求去訪問後端。

  6. 執行緒池。

  7. 模組拆分。將一個專案按功能模組拆分,一個介面也可以按業務粒度進行拆分。

  8. 資料中心。提供資料支撐,譬如黑名單。

  9. 資料庫。加索引、分庫、分表、讀寫分離

  10. 分散式快取。資料分片、拆分大key,並做叢集,採用分散式鎖

  11. MQ。做介面拆分利器,非同步操作。

  12. 其他服務。限流、防刷以及降級(特別是第三方服務,保證第三方服務down掉不要影響我們自身的服務)。在這裡也需要考慮做第三方資料的快取或者持久化,譬如實名認證、身份證認證等。

  13. 監控。監控永遠是必須的,能讓你第一時間知道介面服務是否ok

個人小分享

1)介面Restful,統一返回格式,約定業務層錯誤編碼,每個編碼可以攜帶可選的錯誤資訊

在前司,客戶端和服務之間是有統一的資料返回格式,約定各層的編碼,可以通過編碼位數以及編碼就可以看出是那一層出問題。

我覺得這對我們定位問題以及維護來說具有莫大的意義,並對異常也進行捕捉,封裝成對應的 code,我之前閱讀一些人的程式碼發現其專案根本沒有做這一層,因為簡單而不做我覺得有失所望。

2)採用 hybird 模式

採用 hybird 模式涉及到資源預載入的問題,在很多專案裡面都大量使用,譬如前司的生活服務,就採用了 hybird 模式,先將資原始檔(包含圖片、前端頁面)打包放到伺服器並通過版本號進行管理,並通過一個總的配置檔案來管理,如果是H5頁面可以進行模板預先設計,down到本地。

配置檔案格式:

  *檔案1*        name:xxx        url:http:xxxx        md5:xxxx   *檔案2*        name:zzz        url:http:zzzz        md5:zzz

客戶端每次啟動應用或者定時請求總的配置檔案,通過http code是否是304判斷是否需要下載這個總的配置檔案,如果code是200,那麼下載這個配置,比較那個檔案發生變化,並將其下載。這樣的好處:

  1. 減少介面的互動;

  2. 資源預載入,節省流量,開啟頁面更加流暢,對於服務端來說字需要返回資料json串就行,而不需要其他,減少服務端壓力;

  3. 方便開發人員,資源管理更加簡潔,比如做活動需要的h5頁面,只需要前端上傳對應的h5資源包到服務端,不需要通過後端開發人員就可以搞定。

雖然這個原理很簡單,但是現在很多app還是沒有做這個,都是通過填寫一個url,載入網頁的方式去開啟,體驗性太不友好。

3)客戶端

客戶端跟服務端就是介面請求的關係,很多時候需要要求客戶端做一些資料快取的工作以及一些檢驗工作。在前司已經好幾次給客戶端的同學坑過了,客戶端同學介面亂呼叫,死迴圈呼叫。

一次是做一個關於事件提醒的功能,需要每天定時呼叫呼叫服務端一個介面,結果客戶端的同學寫了一個 bug 導致請求每隔一兩秒就呼叫一次,導致伺服器這邊此介面 pv 翻了N倍,而且這個 bug 通過測試同學很難測試出來;

還有一次發現服務端一段時間以後 UV 不見漲,但是PV卻漲的很猛,定位發現是客戶端同學A圖省事在一個方法裡面呼叫了N個介面,也就是模板方法。

因為版本更新,同學B需要做一個新的功能,然後也呼叫了A同學的介面導致,從而導致PV上升,其實B同學完全不需要呼叫這麼多介面。這些都是真實案例,所以這裡需要有一個監控介面異常的機制。

4)思辨大於執行

寫到這裡覺得這個非常重要,思辨大於執行,意味著我們不是一股腦就去幹,也不是不去幹,我們做事情需要思考、辨別;從而讓事情更高效、更好、更有力的執行。介面設計也一樣,需要我們去思辨。

5)本地快取、分散式快取以及非同步

快取在前司主要分為客戶端快取、CDN快取、本地快取(guava)、Redis快取。

在MZ早期是介面是採用 DB+本地快取的方式提供資料,但這種模式DB壓力大,介面吞吐量小,本地快取多機難一致性、更新不及時問題。

為了解決這些問題,引入分散式快取,並通過 Task 將業務資料刷到 Redis,介面只訪問 redis,不會訪問 DB,及時 DB 故障也不會影響功能。

不同的業務系統系統通過 MQ 來解耦,多機房不是通過 MQ 來實現資料的一直。

比如,評論,先通過寫 Redis,寫 MQ 來實現資料在多機房同步,再通過 task 將 Redis 中評論同步到 DB 中。

介面設計涉及方方面面,這邊也只談到一個大概,雖然有點泛泛而談,希望此拙文對你有所啟示。

6)資料庫

資料庫分庫分表,一般都是通過 userId 或者 imei 或者 mac 地址來分表,單表資料量控制在500w以內,這需要我們提前估算好資料量,儘量避免資料的遷移。

在前司,資料庫一般都是採用 mysql+MongoDB 兩種,MySQL儲存使用者的使用者資料,MongoDB 儲存業務資料,就像閱讀和生活服務裡面的業務資料就儲存在 MongoDB 裡面。

在資料庫這層,我們主要也是通過主從模式、讀寫分離、分庫、分表來實現資料的可用性。

7)業務

業務儘可能拆分、獨立部署、將專案按業務劃分、按功能劃分等。譬如生活服務,我們當時主要拆分成管理後臺 admin、任務 task、活動、web、資料展示模組。

8)資料中心

每個大一點的公司都有資料部門,我們這邊可以通過資料中心的資料分析來達到我們需要的資料。

比如黑名單,推廣效果、活動資料。我們可以通過這些完善我們的介面功能。之前在前司做了個數據處理後非同步載入到 Redis 來實現資料利用的專案。

以上都是我個人的一些拙見,請大家思辨。

 近期熱文

自動化構建

從 Gradle 開始

0?wx_fmt=jpeg

「閱讀原文」看交流實錄,你想知道的都在這裡