1. 程式人生 > >如何設計出高可用、高性能的接口

如何設計出高可用、高性能的接口

數據量 每天 避免 擴展性 客戶端 響應時間 tps 本地 七層

接口設計需要考慮哪些方面

  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:監控。高可用的保證,基於第一位。

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

技術分享圖片

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

如何設計出高可用、高性能的接口