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