說到接口的設計大部分人第一個想到的可能是REST;的確,REST是目前最為普遍的一種接口設計方式,并且作為一個優秀的接口設計標準而被廣泛使用,但是除此之外,我們也不應該忘記還有其他的選項。除了REST,我們還有rpc(或者grp),最近大火GraphQL,以及webhooks. 為了好的了解這幾種設計以及背后的優缺點,我們一一做簡單的介紹。
REST
首先REST--Resource Representational State Transfer, 中文直譯就是資源在網絡中以某種表現形式進行狀態轉移;光這么看確實還是比較頭大,每個單詞拆開可能比較好理解;
Resource:資源,那對于程序來講就是數據;
Representational:表現形式,我們在web開發中常用的傳輸類型比如TXT、HTML、JSON、XML、JPEG等;
State Transfer:狀態變化,對應的是HTTP協議中的動詞(常用的動詞如:GET POST PUT PATCH DELETE);
REST基于HTTP,所以也是無狀態的,以HTTP的各種動詞來定義約定一系列的URL來操作資源;它描述的是網絡中client與server的一種交互形式。
但是我們在談REST的時候其實并不是談論REST本身,而是RESTful API,即REST 風格的網絡接口設計;來看幾個最基礎RESTful API的URL:
GET /api/users : 列出所有用戶
POST /api/users : 新建一個用戶
GET /api/users/ID : 獲取一個用戶的指定信息
PUT /api/user/ID : 更新某個指定用戶的信息(全部信息)
PATH /api/users/ID :更新某個指定用戶的信息(部分信息)
DELETE /api/users/ID : 刪除某個用戶
從上面的例子我們也可以看出RESTful API的設計的URI使用的基本都是名字,具體動詞其實是依賴HTTP中的各個動詞來指定不同的動作(這也是在設計RESTful API時容易產生誤用的地方,會把動詞放在URI上);
REST的優點也比較明顯:客戶端與服務器分離,簡化服務器邏輯以及提高可伸縮性;無狀態,降低服務器資源使用(相對于有狀態的長連接),同時提高服務器的可擴展性;由于不同的信息返回時可以分開標記是否可以緩存,使得客戶端可以重用之前的信息,減少客戶端與服務端的交互次數。
RPC / gRPC
首先RPC-- Remote Procedure Call(遠程過程調用),相信大家不會陌生,主要是用于服務器之間的方法調用。RPC的本質是提供輕量無感知的跨進程通信方式,與上面基于HTTP的RESTful API并不是對立的,并且應用的場景也有所區別;http的接口優點是簡單、直接、開發方便,利用現成http協議進行傳輸;相對于RPC,如果是基于TCP協議的長連接,不必每次都像http 一樣3次握手,減少網絡開銷;其次一般rpc框架都有注冊中心、監控管理,對于服務化架構和服務化治理,RPC框架是 一個強力的支撐;RPC低耗、高效的服務調用方式比較適合 IOT 等對資源、帶寬、性能敏感的場景。
常用的一些分布式RPC框架有Dubbo、Thrift、RPCx等;不過我們這里并不打算介紹這些框架,主要介紹一下谷歌開源的一個rpc框架:gRPC。
gRPC相對于常用的RPC框架最大的特點是使用了protobufs作為語言格式化數據,進一步提高了序列化和反序列化的速度,同時降低數據包的大小。Protobuf開源已久,它提供了一種靈活、高效、自動序列化結構數據的機制,作用與XML、JSON等格式類似,但是使用二進制傳輸,序列化/反序列化的速度快,壓縮效率高;而且Protobuf有強大的IDL(Interface
Description Language,接口描述語言)和相關的工具,用戶寫好.proto描述文件之后可以編譯成多種語言。
gPRC另外一個特點是使用HTTP2,性能比HTTP1.1好很多,我們也可以簡單了解下HTTP2的一些特性,為什么會比HTTP1.1性能好:
新的二進制格式。x都是基于文本解析,而因為文本表現形式的多樣性,基于文本協議的格式解析天然存在健壯性的問題,而采用二進制格式后實現方便并且健壯。
多路復用。x每個請求要重新建立新的連接,而且每個瀏覽器都有會限制單個頁面創建的連接數,而在HTTP2.0中多個請求可以復用一個連接。
header壓縮。目前x中的header信息每次都會重復發送,造成很大的浪費。HTTP2.0使用encoder減少傳輸的header 大小,且通信雙方都緩存一份包含了header信息的表,伺候的請求只需要發送差異數據,避免重復的傳輸。
GraphQL
官方定義GraphQL是一種針對API的查詢語言也是 一個滿足你數據查詢的運行時。可以理解為另外一個種客戶端與服務器之間的交互方式:前端決定后端的返回結果。它的好處是精簡響應的內容,不會出現冗余字段,前端需要什么就取什么,而不需要重新定制開發api。GraphQL并不是REST的替代品,官網上有一張圖描述了GraphQL與其他幾種API設計的關系:
事實上GraphQL跟REST也是可以共存的,甚至可以共同使用一些公共授權驗證模塊驗證調用合法性。
可以找一個簡單的例子看一下GraphQL是如何工作的,比如我們要查詢某個部門下的成員, 用REST的風格可能如下:
curl -v?http://api-host.com/api/deparment/:departmentId/members
含義明確,但是返回什么內容如果沒有溝通過或者沒有文檔說明,客戶端是不知道返回什么內容的。同樣的API GraphQL的做法如下:
query {
departmentn( ) {
members(first: 100) {
edges {
node {
name avatarUrl
}
}
}
}
}
具體返回的結果如下:
{
"data": {
"department": {
"members": {
"edges": [
{
"node": {
"name": "member1",
"avatarUrl": "https://test/member1"
}
},
{
"node": {
"name": "member2",
"avatarUrl": "https://test/member2"
}
}
]
}
}
}
}
從上面返回的內容可以看出,不會包含多余的內容,只返回request需要的內容。目前使用GraphQL的一個比較典型和熟知的例子就是github, github api v4 開始使用graphql,有興趣可以了解下。
實現了GraphQL標準的客戶端有很多,比如Relay或者appollo-client等。
Webhook
如果說上面的GraphQL是REST的補充,改變了前后端的交互模式,gRPC快速、 高效的 方式賦予了客戶端更強大的能力,那么webhook可能跟之前的這些方式改變得更徹底,客戶端不再主動發送請求,而是完全由后端進行推送,可以說webhook是傳統client-server模式徹底的反模式了。
比如你的客戶端要長期監聽某個任務的狀態,如果按照正常的api調用的方式去做,那么必須不停得輪訓服務器來獲取當前狀態;使用webhook則無需輪訓,只需要等待服務器推送信息過來,客戶端更新即可。git webhook其實也是這方面的應用。
結論
從以上的介紹可以看出,其實并沒有哪種方法是必然優于另外一種的,每一種方法都有其適 用的場景。
REST: 無狀態的數據傳輸,適用于通用、快速迭代和標準化語義的場景;雖然 RESTful API的設計風格是目前最普遍的方式,但是其實也并不是所有場景都適合, 比如遇到有復雜操作要求的前端交互,用起來就很別扭,像獲取、刪除多個對象;對于一些內部的系統,大多數開發更注重效率,而不是完全遵守這樣的規范,真正完全遵守的場景確實也不多。
gRPC:對于前端跟服務器端交互來說HTTP 無狀態的方式其實是最方便的,前后端完全解耦,系統更容易擴展。而對于一些服務端之間的通信,比如現在的微服務,大部分共用服務可能并不需要對外開放,只在內部進行相互調用,這種情況下gRPC是個不錯的選擇,而且也能滿足服務器之間調用對性能和延時要求高的需求。
GraphQL:GraphQL 并不是作為REST的替代方案,如果指望使用GraphQL能答復提升開發效率,那可能不現實;其實讓客戶端來選擇返回的內容增加了客戶端的工作量,把服務器端的一些工作轉移出去了。不過相對于那些相對成熟的對外開放的OpendAPI,GraphQL更合適,比如github api,可以讓開發者覺得獲取內容其實是非常高效的,因為對于穩定的內容,有選擇的去獲取數據比全量返回從效率上能提升不少。
Webhook:其實從字面上理解這種應用場景就是需要服務器主動推送的地方,比如沒有前端界面作為中轉的服務,或者是不適合前端來操作的強安全頁面(如支付),算是在特殊場景下的應用方式,畢竟相對于無狀態的方式,維持連接來做推送還是開銷不小,會增加不少的服務器端壓力。
在技術選型上可能是一個項目開始的時候最艱難的事,不同的應用場景要選擇不同的技術解決 方案,畢竟在軟件開發上沒有銀彈,合適的才是最好的。
評論
查看更多