如何設計出高可用、高性能的介面

  • 2020 年 3 月 18 日
  • 筆記

介面設計怎麼樣才能高可用,高性能?

—————–來自小馬哥的故事


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

  1. 介面的命名。
  2. 請求參數。
  3. 支援的協議。
  4. TPS、並發數、響應時長。
  5. 是否需要白名單。
  6. 數據存儲。DB選型、快取選型。
  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:監控。高可用的保證,基於第一位

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

  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來實現數據利用的項目。

本文由 小馬哥 創作,採用 知識共享署名4.0 國際許可協議進行許可 本站文章除註明轉載/出處外,均為本站原創或翻譯,轉載前請務必署名