精读《REST,GraphQL,Webhooks gRPC 如何选型》
1 引言
每當項目進入聯(lián)調(diào)階段,或者提前約定接口時,前后端就會聚在一起熱火朝天的討論起來。可能 99% 的場景都在約定 Http 接口,討論 URL 是什么,入?yún)⑹鞘裁?#xff0c;出參是什么。
有的團隊前后端接口約定更加高效,后端會拿出接口定義代碼,前端會轉(zhuǎn)換成(或自動轉(zhuǎn)成)Typescript 定義文件。
但這些工作都針對于 Http 接口,今天通過 when-to-use-what-rest-graphql-webhooks-grpc 一文,拋開聯(lián)調(diào)時千遍一律的 Http 接口,一起看看接口還可以怎么約定,分別適用于哪些場景,你現(xiàn)在處于哪個場景。
2 概述
本文主要講了四種接口設(shè)計方案,分別是:REST、gRPC、GraphQL、Webhooks,下面分別介紹一下。
REST
REST 也許是最通用,也是最常用的接口設(shè)計方案,它是 無狀態(tài)的,以資源為核心,針對如何操作資源定義了一系列 URL 約定,而操作類型通過 GET POST PUT DELETE 等 HTTP Methods 表示。
REST 基于原生 HTTP 接口,因此改造成本很小,而且其無狀態(tài)的特性,降低了前后端耦合程度,利于快速迭代。
隨著未來發(fā)展,REST 可能更適合提供微服務(wù) API。
使用舉例:
curl -v -X GET https://api.sandbox.paypal.com/v1/activities/activities?start_time=2012-01-01T00:00:01.000Z&end_time=2014-10-01T23:59:59.999Z&page_size=10 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer Access-Token"
gRPC
gRPC 是對 RPC 的一個新嘗試,最大特點是使用 protobufs 語言格式化數(shù)據(jù)。
RPC 主要用來做服務(wù)器之間的方法調(diào)用,影響其性能最重要因素就是 序列化/反序列化 效率。RPC 的目的是打造一個高效率、低消耗的服務(wù)調(diào)用方式,因此比較適合 IOT 等對資源、帶寬、性能敏感的場景。而 gRPC 利用 protobufs 進一步提高了序列化速度,降低了數(shù)據(jù)包大小。
使用舉例:
gRPC 主要用于服務(wù)之間傳輸,這里拿 Nodejs 舉例:
1.定義接口。由于 gRPC 使用 protobufs,所以接口定義文件就是 helloword.proto:
// The greeting service definition.
service Greeter {
?// Sends a greeting
?rpc SayHello (HelloRequest) returns (HelloReply) {}
?// Sends another greeting
?rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
?string name = 1;
}
// The response message containing the greetings
message HelloReply {
?string message = 1;
}
這里定義了服務(wù) Greeter,擁有兩個方法: SayHello 與 SayHelloAgain,通過 message 關(guān)鍵字定義了入?yún)⑴c出參的結(jié)構(gòu)。
事實上利用 protobufs,傳輸數(shù)據(jù)時僅傳送很少的內(nèi)容,作為代價,雙方都要知道接口定義規(guī)則才能序列化/反序列化。
2.定義服務(wù)器:
function sayHello(call, callback) {
?callback(null, { message: "Hello " + call.request.name });
}
function sayHelloAgain(call, callback) {
?callback(null, { message: "Hello again, " + call.request.name });
}
function main() {
?var server = new grpc.Server();
?server.addProtoService(hello_proto.Greeter.service, {
? ?sayHello: sayHello,
? ?sayHelloAgain: sayHelloAgain
?});
?server.bind("0.0.0.0:50051", grpc.ServerCredentials.createInsecure());
?server.start();
}
我們在 50051 端口支持了 gRPC 服務(wù),并注冊了服務(wù) Greeter,并對 sayHello sayHelloAgain 方法做了一些業(yè)務(wù)處理,并返回給調(diào)用方一些數(shù)據(jù)。
3.定義客戶端:
function main() {
?var client = new hello_proto.Greeter(
? ?"localhost:50051",
? ?grpc.credentials.createInsecure()
?);
?client.sayHello({ name: "you" }, function(err, response) {
? ?console.log("Greeting:", response.message);
?});
?client.sayHelloAgain({ name: "you" }, function(err, response) {
? ?console.log("Greeting:", response.message);
?});
}
可以看到,客戶端和服務(wù)端同時需要拿到 proto 結(jié)構(gòu),客戶端數(shù)據(jù)發(fā)送也要依賴 proto 包提供的方法,框架會內(nèi)置做掉序列化/反序列化的工作。
也有一些額外手段將 gRPC 轉(zhuǎn)換為 http 服務(wù),讓網(wǎng)頁端也享受到其高效、低耗的好處。但是不要忘了,RPC 最常用的場景是 IOT 等硬件領(lǐng)域,網(wǎng)頁場景也許不會在乎節(jié)省幾 KB 的流量。
GraphQL
GraphQL 不是 REST 的替代品,而是另一種交互形式:前端決定后端的返回結(jié)果。
GraphQL 帶來的最大好處是精簡請求響應(yīng)內(nèi)容,不會出現(xiàn)冗余字段,前端可以決定后端返回什么數(shù)據(jù)。但要注意的是,前端的決定權(quán)取決于后端支持什么數(shù)據(jù),因此 GraphQL 更像是精簡了返回值的 REST,而后端接口也可以一次性定義完所有功能,而不需要逐個開發(fā)。
再次強調(diào),相比 REST 和 gRPC,GraphQL 是由前端決定返回結(jié)果的反模式。
使用舉例:
原文推薦參考 GitHub GraphQL API
比如查詢某個組織下的成員,REST 風(fēng)格接口可能是:
curl -v https://api.github.com/orgs/:org/members
含義很明確,但問題是返回結(jié)果不明確,必須實際調(diào)試才知道。換成等價的 GraphQL 是這樣的:
query {
?organization(login: "github") {
? ?members(first: 100) {
? ? ?edges {
? ? ? ?node {
? ? ? ? ?name
? ? ? ? ?avatarUrl
? ? ? ?}
? ? ?}
? ?}
?}
}
返回的結(jié)果和約定的格式結(jié)構(gòu)一致,且不會有多余的字段:
{
?"data": {
? ?"organization": {
? ? ?"members": {
? ? ? ?"edges": [
? ? ? ? ?{
? ? ? ? ? ?"node": {
? ? ? ? ? ? ?"name": "Chris Wanstrath",
? ? ? ? ? ? ?"avatarUrl": "https://avatars0.githubusercontent.com/u/2?v=4"
? ? ? ? ? ?}
? ? ? ? ?},
? ? ? ? ?{
? ? ? ? ? ?"node": {
? ? ? ? ? ? ?"name": "Justin Palmer",
? ? ? ? ? ? ?"avatarUrl": "https://avatars3.githubusercontent.com/u/25?v=4"
? ? ? ? ? ?}
? ? ? ? ?}
? ? ? ?]
? ? ?}
? ?}
?}
}
但是能看出來,這樣做需要一個系統(tǒng)幫助你寫 query,很多框架都提供這個功能,比如 apollo-client。
Webhooks
如果說 GraphQL 顛覆了前后端交互模式,那 Webhooks 可以說是徹頭徹尾的反模式了,因為其定義就是,前端不主動發(fā)送請求,完全由后端推送。
它最適合解決輪詢問題。或者說輪詢就是一種妥協(xié)的行為,當后端不支持 Webhooks 模式時。
使用舉例:
Webhooks 本身也可以由 REST 或者 gRPC 實現(xiàn),所以就不貼代碼了。舉個常用例子,比如你的好友發(fā)了一條朋友圈,后端將這條消息推送給所有其他好友的客戶端,就是 Webhooks 的典型場景。
最后作者給出的結(jié)論是,這四個場景各有不同使用場景,無法相互替代:
-
REST:無狀態(tài)的數(shù)據(jù)傳輸結(jié)構(gòu),適用于通用、快速迭代和標準化語義的場景。
-
gRPC:輕量的傳輸方式,特殊適合對性能高要求或者環(huán)境苛刻的場景,比如 IOT。
-
GraphQL: 請求者可以自定義返回格式,某些程度上可以減少前后端聯(lián)調(diào)成本。
-
Webhooks: 推送服務(wù),主要用于服務(wù)器主動更新客戶端資源的場景。
3 精讀
REST 并非適用所有場景
本文給了我們一個更大的視角看待日常開發(fā)中的接口問題,對于奮戰(zhàn)在一線的前端同學(xué),接觸到 90% 的接口都是非 REST 規(guī)則的 Http 接口,能真正落實 REST 的團隊其實非常少。這其實暴露了一個重要問題,就是 REST 所帶來的好處,在整套業(yè)務(wù)流程中到底占多大的比重?
不僅接口設(shè)計方案的使用要分場景,針對某個接口方案的重要性也要再繼續(xù)細分:在做一個開放接口的項目,提供 Http 接口給第三方使用,這時必須好好規(guī)劃接口的語義,所以更容易讓大家達成一致使用 REST 約定;而開發(fā)一個產(chǎn)品時,其實前后端不關(guān)心接口格式是否規(guī)范,甚至在開發(fā)內(nèi)網(wǎng)產(chǎn)品時,性能和冗余都不會考慮,效率放在了第一位。所以第一點啟示是,不要埋冤當前團隊業(yè)務(wù)為什么沒有使用某個更好的接口約定,因為接口約定很可能是業(yè)務(wù)形態(tài)決定的,而不是憑空做技術(shù)對比從而決定的。
gRPC 是服務(wù)端交互的首選
前端同學(xué)轉(zhuǎn) node 開發(fā)時,很喜歡用 Http 方式進行服務(wù)器間通訊,但可能會疑惑,為什么公司內(nèi)部 Java 或者 C++ 寫的服務(wù)都不提供 Http 方式調(diào)用,而是另外一個名字。了解 gRPC 后,可以認識到這些平臺都是對 RPC 方式的封裝,服務(wù)器間通信對性能和延時要求非常高,所以比較適合專門為性能優(yōu)化的 gRPC 等服務(wù)。
GraphQL 需要配套
GraphQL 不是 REST 的替代品,所以不要想著團隊從 Http 接口遷移到 GraphQL 就能提升 X% 的開發(fā)效率。GraphQL 方案是一種新的前后端交互約定,所以上手成本會比較高,同時,為了方便前端同學(xué)拼 query,等于把一部分后端工作量轉(zhuǎn)移給了前端,如果此時沒有一個足夠好用的平臺快速查閱、生成、維護這些定義,開發(fā)效率可能不升反降。
總的來說,對外開放 API 或者擁有完整配套的場景,使用 GraphQL 是比較理想的,但對于快速迭代,平臺又不夠成熟的團隊,繼續(xù)使用標準 Http 接口可以更快完成項目。
Webhooks 解決特殊場景問題
對于第三方平臺驗權(quán)、登陸等 沒有前端界面做中轉(zhuǎn)的場景,或者強安全要求的支付場景等,適合用 Webhooks 做數(shù)據(jù)主動推送。說白了就是在前端無從參與,或者因為前端安全問題不適合參與時,就是 Webhooks 的場景。很顯然 Webhooks 也不是 Http 的替代品,不過的確是一種新的前后端交互方式。
對于慢查詢等場景,前端普遍使用輪詢完成,這和 Socket 相比體驗更弱,但無狀態(tài)的特性反而會降低服務(wù)器負擔(dān),所以慢查詢和即時通訊要區(qū)分對待,用戶對消息及時性的敏感程度決定了使用哪種方案。
4 總結(jié)
最后,上面總結(jié)的內(nèi)容一定還有許多疏漏,歡迎補充。
5 更多討論
討論地址是:精讀《REST, GraphQL, Webhooks, & gRPC 如何選型》 · Issue #102 · dt-fe/weekly
原文發(fā)布時間為:2018-11-03 本文作者:黃子毅??
本文來自云棲社區(qū)合作伙伴“前端大學(xué)”,了解相關(guān)信息可以關(guān)注“前端大學(xué)”。
總結(jié)
以上是生活随笔為你收集整理的精读《REST,GraphQL,Webhooks gRPC 如何选型》的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【hadoop】hive 安装实践
- 下一篇: maven scope 属性和生命周期