API設(shè)計中性能提升的十個建議
節(jié)前的時候, 一好友約我聊一聊API 的設(shè)計。當(dāng)時覺得仿佛有萬語千言,但我又難以脫口而出1、2、3。原來,即便是工作的日常,也缺乏一個系統(tǒng)性的思考和整理。API的設(shè)計涉及到的方面很多, 分類是一個基本的思考方式。如果可以形成一個系列性的文字,那就從性能開始吧。
就像任何性能一樣,API 性能主要取決于如何響應(yīng)不同類型的請求。例如:典型的電商場景,顯示用戶當(dāng)前的訂單。應(yīng)用程序從一個 API 獲取訂單詳情。但是,如果希望用戶在一個地方能查看他們所有的訂單,這意味著,我們的 API 現(xiàn)在將返回比以前更多的數(shù)據(jù),后臺的負(fù)載會更大。如何確保我API 能夠?qū)⑺袛?shù)據(jù)返回給用戶,而不會出現(xiàn)延遲、服務(wù)器錯誤和過多請求等問題呢?
一般地, 如何在API設(shè)計中提升性能呢?還沒有梳理出完整的方法論,但就REST API 而言,根據(jù)多年的經(jīng)驗和教訓(xùn),這里總結(jié)了10點建議。
1.啟用日志,建立監(jiān)控
API 的監(jiān)控是最重要的,沒有之一。
擁有日志、監(jiān)控和告警可以幫助我們在潛在問題變成真正問題之前診斷并糾正問題。如果沒有啟用日志記錄,并且存在潛在問題,那么我們將無法跟蹤性能指標(biāo),或者在特定請求中定位問題發(fā)生的位置。奢侈一點的話, 要嘗試全鏈路跟蹤系統(tǒng),盡管成本較高,但物有所值。
2.提升網(wǎng)速,帶寬足夠
即便API設(shè)計的性能非常強(qiáng)大,也扛不住緩慢的網(wǎng)絡(luò)延遲。不可靠的網(wǎng)絡(luò)可能會導(dǎo)致宕機(jī),導(dǎo)致違反SLA、服務(wù)條款以及曾經(jīng)向客戶做出的承諾。必須要投資適當(dāng)?shù)木W(wǎng)絡(luò)基礎(chǔ)設(shè)施,以便我們能夠維持所需的性能水平,有時候,可以通過利用和購買足夠的云資源來實現(xiàn)。
3.減少有效負(fù)載
如果響應(yīng)數(shù)據(jù)的有效負(fù)載非常大,將會減慢請求完成的時間,并影響性能。簡單地,使用 GZip 壓縮來減少有效載載的大小,可以在 Web API 上使用 Deflate 壓縮,或者,可以將 Accept-EncodingRequest 更新為 gzip。有效的數(shù)據(jù)壓縮減少了在 web 應(yīng)用程序上響應(yīng)的下載量,同時提高了上傳速度。
4.使用緩存
緩存是提高 API 性能的最簡單的方法之一。如果有經(jīng)常返回相同響應(yīng)的請求,那么該響應(yīng)的緩存有助于避免額外的服務(wù)調(diào)用和數(shù)據(jù)庫查詢。需要注意的是,確保在緩存使用的生命周期,尤其是在發(fā)生數(shù)據(jù)更新的時候。緩存增強(qiáng)了可伸縮性。服務(wù)端可以通過設(shè)置響應(yīng)頭來提高緩存能力,比如 Cache-Control、 Expires、 Pragma、 Last-Modified 等等。
5.流控與頻控
API 遭受 DDoS 攻擊是惡意的,但有時候,也可能是工程師無意造成的,例如當(dāng)工程師在某個本地應(yīng)用程序的循環(huán)中執(zhí)行調(diào)用 API 的調(diào)用。一般地,可以通過監(jiān)視每個 IP 地址或每個 SSO令牌發(fā)生的事務(wù)數(shù)量來避免這種情況。頻控和流控都是為了性能而實現(xiàn)限速的方式,有助于處理API的意外調(diào)用,并主動監(jiān)視和識別可能的惡意活動,也是實現(xiàn)安全性的重要手段。
6.嘗試HTTP 的標(biāo)準(zhǔn)方法
嘗試使用HTTP 的標(biāo)準(zhǔn)方法,對API 的性能會有一定的幫助。
http 方法 | 冪等性 | 是否安全 |
get | 是 | 是 |
head | 是 | 是 |
put | 是 | 否 |
delete | 是 | 否 |
post | 否 | 否 |
patch | 否 | 否 |
例如,PUT 和 PATCH 操作在更新資源方面是相似的,但執(zhí)行更新的方式是不同的。PUT 操作通過向整個資源發(fā)送更新來更新資源。PATCH 操作只對需要更新的資源應(yīng)用部分更新。由此產(chǎn)生的 PATCH 調(diào)用可以產(chǎn)生更小的有效負(fù)載,從而提高性能。需要注意的是PATCH 調(diào)用可不是冪等的。
7.嘗試標(biāo)準(zhǔn)的HTTP 狀態(tài)碼
我們可以進(jìn)一步對響應(yīng)進(jìn)行標(biāo)準(zhǔn)化、細(xì)分和限制,這有助于降低結(jié)果的復(fù)雜性,并通過僅針對客戶所要求的內(nèi)容提供響應(yīng)/結(jié)果來改善整個客戶體驗。嘗試使用標(biāo)準(zhǔn)的HTTP 狀態(tài)碼是一種不錯的方式,顯然,響應(yīng)的狀態(tài)由其狀態(tài)代碼指定: 1xx 表示信息,2xx 表示成功,3xx 表示重定向,4xx 表示客戶機(jī)錯誤,5xx 表示服務(wù)器錯誤。例如,使用 HTTP狀態(tài)碼,并且只使用響應(yīng)體提供錯誤細(xì)節(jié)。
HTTP/1.2 400 Bad RequestContent-Type: application/json{ "error": "Expected xxx in xxx"}
8.動靜分離,使用CDN
此外,如果有大量的后臺進(jìn)程進(jìn)程,可以在單獨的線程上運行這些進(jìn)程,以避免阻塞請求。常見的一種方式是將API請求中的靜態(tài)資源分離開了,可以使用內(nèi)容交付網(wǎng)絡(luò)(CDN)來更快地服務(wù)不同地區(qū)請求中的靜態(tài)資源。
9.啟用分頁,過濾排序
對于大型數(shù)據(jù)集,限制返回的數(shù)據(jù)量是至關(guān)重要的。此外,可能希望指定要包含在響應(yīng)中的資源的字段或?qū)傩裕瑥亩拗品祷氐臄?shù)據(jù)量,最終希望查詢特定的值并對返回的數(shù)據(jù)進(jìn)行排序。
分頁有助于從多個響應(yīng)創(chuàng)建大量的內(nèi)容。這種優(yōu)化有助于改進(jìn)響應(yīng),同時保存?zhèn)鬏?顯示給客戶的數(shù)據(jù)。當(dāng)鏈接在響應(yīng)中返回時,api 變得更具自我描述性。對于在支持分頁的響應(yīng)中返回的集合,“ first”、“ last”、“ next”和“ prev”鏈接至少是有益的。
10.使用性能更優(yōu)的應(yīng)用框架
應(yīng)用框架的使用是為了提高開發(fā)效率,如果為了性能進(jìn)行適當(dāng)?shù)膬?yōu)化和增強(qiáng),也可以為業(yè)務(wù)和客戶提供極其強(qiáng)大的體驗。一些框架專門用于構(gòu)建 REST API,可以幫助我們在不犧牲生產(chǎn)力的前提下提升性能。例如,在python中,web 應(yīng)用框架眾多,例如 Django ,F(xiàn)lask,Tornado,F(xiàn)alcon等等。就性能而言,F(xiàn)alcon和Tornado可能是不錯的選擇。在 Node 中,Restify 似乎也不錯的選擇,但是我還沒有在生產(chǎn)環(huán)境來嘗試它,基于Go 語言的應(yīng)用框架更是非常值得關(guān)注的。
總之,業(yè)務(wù)需求和客戶期望總是隨著時間而變化的,我們有責(zé)任決定如何以高效的方式構(gòu)建高性能的API,這樣可以幫助我們實現(xiàn)并超越目標(biāo)。根據(jù)特定的 API 和用例,確定API與什么服務(wù)交互,以及調(diào)用的頻率,從哪里調(diào)用等等,我們可以用不同的方式實現(xiàn)高性能的API。