如何設計出高可用、高性能的接口
接口設計需要考慮哪些方面
- 接口的命名。
- 請求參數。
- 支持的協議。
- TPS、並發數、響應時長。
- 數據存儲。DB選型、緩存選型。
- 是否需要依賴於第三方。
- 接口是否拆分。
- 接口是否需要冪等。
- 防刷。
- 接口限流、降級。
- 負載均衡器支持。
- 如何部署。
- 是否需要服務治理。
- 是否存在單點。
- 接口是否資源包、預加載還是內置。
- 是否需要本地緩存。
- 是否需要分布式緩存、緩存穿透怎麽辦。
- 是否需要白名單。
當我們設計接口,我們或多或少都會有上面列舉的一些考慮,我們只有想的更多才能讓讓我們的接口更加完善,我個人覺得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:監控。高可用的保證,基於第一位。
下圖是從一個基本的請求出發來梳理需要涉及到各個段,以及各個端能做的事情。談談接口服務,但不局限於接口本身。
-
客戶端:資源預加載、限制請求、數據上報。我這邊就拿客戶端來舉個例子。接口服務所依賴的資源包或者一些公共配置預加載在本地,減少接口的交互,通過請求配置文件是否更新,code是否是304等來;接口做一些請求限制,比如搶紅包、搶券等,單位時間內N次點擊只請求一次等;接口失敗數據上報來;這就是客戶端可以做到的對接口有幫助的事情
-
GSLB/HttpDNS:多機房部署、流量切換、域名劫持,一般技術和業務比較成熟的公司這一層。
-
資源文件放CDN。
-
負載均衡器:lVS+Nginx是互聯網常用的做負載均衡,可以實現四層/七層負載均衡;這裏除了可以分流、轉發以外,我們用的更多的基於令牌桶限流、緩存。
-
本地緩存。本地緩存能減少我們訪問DB或者分布式緩存,本地緩存推薦使用guava,guava裏面有很多特性很好用,例如基於令牌桶的限流;當緩存失效時只穿透一個請求去訪問後端。
-
線程池。
-
模塊拆分。將一個項目按功能模塊拆分,一個接口也可以按業務粒度進行拆分。
-
數據中心。提供數據支撐,譬如黑名單。
-
數據庫。加索引、分庫、分表、讀寫分離
-
分布式緩存。數據分片、拆分大key,並做集群,采用分布式鎖
-
MQ。做接口拆分利器,異步操作。
-
其他服務。限流、防刷以及降級(特別是第三方服務,保證第三方服務down掉不要影響我們自身的服務)。在這裏也需要考慮做第三方數據的緩存或者持久化,譬如實名認證、身份證認證等。
-
監控。監控永遠是必須的,能讓你第一時間知道接口服務是否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,那麽下載這個配置,比較那個文件發生變化,並將其下載。這樣的好處:
- 減少接口的交互;
- 資源預加載,節省流量,打開頁面更加流暢,對於服務端來說字需要返回數據json串就行,而不需要其他,減少服務端壓力;
- 方便開發人員,資源管理更加簡潔,比如做活動需要的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來實現數據利用的項目。
如何設計出高可用、高性能的接口