一、請求--響應API。
請求--響應類的API的典型做法是,通過基於HTTP的Web伺服器暴露一個/套介面。API定義一些端點,客戶端傳送資料的請求到這些端點,Web伺服器處理這些請求,然後返回響應。響應的格式通常是JSON或XML。
在這種型別的Web API裡,比較流行的是這三種:REST,RPC和GraphQL。
1.1 REST
REST全稱是Representational State Transfer 表述性狀態傳遞。REST可能是現在最流行的一種Web API。
REST的核心就是資源,一個資源就是可以被標識的實體,它有名稱和地址。
REST API就是把資料以資源的形式暴露出來,並使用標準的HTTP方法來代表建立、讀取、更新和刪除資源等事務。
REST API有一些規則和約束,這裡我就簡單的寫一下(之前的文章有詳細描述):
-
資源都是URL的一部分,例如/persons
-
針對每個資源通常都會有兩個URL被實現:“/persons”表示資源的集合,“/person/321”表示特定的一個資源
-
在資源裡,使用名詞而不是動詞,例如 /getUserInfo/123 這就不對了,應該是 /users/123
-
HTTP方法表明了要執行的動作,不同的HTTP方法作用於同一個URL上可實現不同的功能:
-
建立 -- POST
-
讀取 -- GET
-
整體更新 -- PUT
-
區域性更新 -- PATCH
-
刪除 -- DELETE
-
伺服器會返回標準的HTTP狀態碼,來表示請求成功或者失敗,以及原因。通常2xx表示成功,3xx表示資源被移動了,4xx表示客戶端引起的錯誤,5xx表示伺服器端引起的錯誤。
如果兩個資源有主從關係,那麼子資源最好不採用頂級資源的URL,而是採用主資源的子資源URL地址。例如Province和City就是主從關係,那麼City資源的URL應該是:/provinces/{provinceId}/cities,/provinces/{provinceId}/cities/{cityId}
非CRUD操作
API難免會有一個非CRUD的操作,例如“存檔”這個操作。這時候我們可以採取以下幾種辦法:
-
把這個動作作為資源的一個欄位。例如把“存檔”作為輸入引數傳遞到API
-
作為子資源。例如 /repos/{repoId}/issues/{issueId}/archive
-
直接使用動詞。實在不行了,就用動詞吧,例如 /search/params?......
1.2 RPC
Remote Procedure Call。RPC是一種比較簡單的API,客戶端直接會執行另一個伺服器上的程式碼。
REST是關於資源的,而RPC就是關於動作的。
在RPC裡,客戶端通常是把方法名和引數傳遞給伺服器,然後伺服器返回JSON或XML。
RPC的規則比較少:
-
端點要包含被執行操作的名字
-
使用合理的HTTP動詞,GET用於讀取,POST用於其它型別。
RPC適用於那種無法用CRUD封裝的動作,或者其影響和資源無關的動作。
RPC不僅限於HTTP,還有其它協議可以支援,例如Apache Thrift和gRPC。
1.3 GraphQL
GraphQL 是 API的查詢語言。最近越來越火。它由Facebook於2012年開始開發,2015年被開源了。
GraphQL允許客戶端定義需要得到的資料結構,伺服器精確的返回所需的資料結構,例如:
與REST和RPC不同,GraphQL API只需要一個端點;它也不需要使用不同的HTTP動詞,它只使用POST,你需要在JSON body裡面指定是要執行查詢還是修改。
相對REST和RPC,GraphQL有下面幾個優勢:
-
節省了多重的請求往返,GraphQL可以一次把所需的關聯資料全部查詢出來。不會存在例如N+1這樣的問題
-
避免了API版本問題。你可以隨時新增欄位和型別,不會影響現有的查詢。可以標記棄用。通過Log可以追蹤出哪些欄位被誰使用,如果欄位沒人再去使用,就可以移除它了。
-
Payload比較小。REST和RPC的響應都包含客戶端傳送一些不需要的資料。而使用GraphQL的話,客戶端得到的響應就是它所請求的那些東西,不多不少。
-
強型別。GraphQL是強型別的,開發時有型別檢查能保證查詢的正確性和合理性。
-
內省(Introspection)。像REST,就需要安裝Swagger等工具來幫助瀏覽API。而GraphQL本身就具備可發現性。它還帶有一個瀏覽器內的IDE用來瀏覽GraphQL API。下圖就是Github的GraphQL API:
GraphQL的缺點就是它為伺服器新增了許多複雜性,伺服器需要額外的工作來處理這些複雜的查詢。根據查詢內容的不同,效能也是一個變數.
綜上所述,那麼什麼時候應該用哪種Web API呢?
-
針對CRUD類的API,使用REST
-
針對暴露很多動作的API,使用RPC
-
當你需要查詢的靈活性以及維護的連續性時,使用GraphQL
二、事件驅動式 Web API
針對用請求-響應式API,如果服務的資料經常變化,那麼響應就可能無法保持新鮮了。開發者如果想與變化的資料保持同步,就只能對API進行polling操作了。
但是如果poll的頻率較低,客戶端仍有可能無法獲得從上次poll到現在所有的資料事件。如果poll的頻率較高,還特別浪費資源。
所以我們需要實時的分享事件的資料,通常使用下面三種機制:WebHook,WebSocket,HTTP Streaming。
2.1 WebHooks
WebHook就是一個接收HTTP POST(或GET,PUT,DELETE)的URL。一個實現了WebHook的API提供商就是在當事件發生的時候會向這個配置好的URL傳送一條資訊。與請求-響應式不同,使用WebHook,你可以實時接受到變化。
下面是Polling和Webhook的比較:
WebHook非常適合於從一個伺服器向另外一個伺服器分享實時資料。
但是實現WebHook,也引入了新的複雜性:
-
失敗和重試。為了保證WebHook被成功的傳輸,你需要構建一個可以再發生錯誤時進行重試操作的系統。
-
安全性。對於安全的呼叫REST API,現在的方案都比較成熟;而對於WebHook來說,這方面依然在探索中前進。
-
防火牆。防火牆後執行的應用可以通過HTTP訪問API,但是它們可能無法接收入站的流量。所以這是一個很大的問題。
-
噪聲。通常每個WebHook呼叫代表了一個事件,但當短時間內發生了成千上萬個事件的時候,再通過WebHook來傳輸,就可能會有噪音。
2.2 WebSocket
WebSocket這個協議,它通過一個TCP協議建立一個雙向全雙工的流式通訊。WebSocket通常用在客戶端和伺服器之間的通訊,也可以用在伺服器之間的通訊。
ASP.NET Core SignalR就是優先使用該協議。
WebSocket支援全雙工(伺服器和客戶端可以同時雙向通訊),而且開銷不高。經常使用的埠式80或443,這樣就很容易穿過防火牆了。
WebSocket特別適合於快速的,現場的路i資料和長連線。
如果連線掛掉了,客戶端會嘗試重新初始化連線。但是WebSocket有一些擴充套件性的問題,因為如果線上的客戶端太多,那麼伺服器端就需要維持這些客戶端開啟的連線。
2.3 HTTP Streaming
使用請求-響應式API,客戶端傳送一個請求,伺服器端返回一個響應,這個響應的長度是有限的。
而使用HTTP Streaming,伺服器端可以在一個由客戶端開啟的長生存的連線裡持續的推送新資料。
為了讓資料通過一個可長時間存在的連線上進行傳輸,有兩個方案:
-
首先可以讓伺服器把Transfer-Encoding這個Header設為chunked。這表示客戶端是按塊接收資料的,塊與塊之間用換行符分割:“\r\n”。
-
另一個選項是通過Server-Sent Events (SSE)來進行流資料。這個比較適合於瀏覽器內的客戶端,因為這樣它們就可以使用標準的EventSource API了。(SignalR在無法使用WebSocket的時候就會使用SSE)
HTTP Streaming用起來好像很容易,但是有個問題,是關於快取的。客戶端和代理經常會有快取的限制。因為只有達到某個閾值之後,它們才會把資料渲染給應用。
綜上,針對事件驅動式Web API:
-
如果想要進行伺服器間的實時事件通訊,可以選擇WebHooks
-
如果需要瀏覽器和伺服器間的雙向實時通訊,可以選擇WebSocket
-
如果需要使用簡單的HTTP進行單向通訊,可以使用HTTP Streaming