精读《REST, GraphQL, Webhooks, & gRPC 如何选型》

1 引言

每当项目进入联调阶段,或者提早约定接口时,先后端就会聚在一块儿热火朝天的讨论起来。可能 99% 的场景都在约定 Http 接口,讨论 URL 是什么,入参是什么,出参是什么。前端

有的团队先后端接口约定更加高效,后端会拿出接口定义代码,前端会转换成(或自动转成)Typescript 定义文件。node

但这些工做都针对于 Http 接口,今天经过 when-to-use-what-rest-graphql-webhooks-grpc 一文,抛开联调时千遍一概的 Http 接口,一块儿看看接口还能够怎么约定,分别适用于哪些场景,你如今处于哪一个场景。git

2 概述

本文主要讲了四种接口设计方案,分别是:REST、gRPC、GraphQL、Webhooks,下面分别介绍一下。github

REST

REST 也许是最通用,也是最经常使用的接口设计方案,它是 无状态的,以资源为核心,针对如何操做资源定义了一系列 URL 约定,而操做类型经过 GET POST PUT DELETE 等 HTTP Methods 表示。web

REST 基于原生 HTTP 接口,所以改形成本很小,并且其无状态的特性,下降了先后端耦合程度,利于快速迭代。json

随着将来发展,REST 可能更适合提供微服务 API。后端

使用举例: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 语言格式化数据。安全

RPC 主要用来作服务器之间的方法调用,影响其性能最重要因素就是 序列化/反序列化 效率。RPC 的目的是打造一个高效率、低消耗的服务调用方式,所以比较适合 IOT 等对资源、带宽、性能敏感的场景。而 gRPC 利用 protobufs 进一步提升了序列化速度,下降了数据包大小。性能优化

使用举例:

gRPC 主要用于服务之间传输,这里拿 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;
}

这里定义了服务 Greeter,拥有两个方法:SayHelloSayHelloAgain,经过 message 关键字定义了入参与出参的结构。

事实上利用 protobufs,传输数据时仅传送不多的内容,做为代价,双方都要知道接口定义规则才能序列化/反序列化。

2.定义服务器:

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 服务,并注册了服务 Greeter,并对 sayHello sayHelloAgain 方法作了一些业务处理,并返回给调用方一些数据。

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);
  });
}

能够看到,客户端和服务端同时须要拿到 proto 结构,客户端数据发送也要依赖 proto 包提供的方法,框架会内置作掉序列化/反序列化的工做。

也有一些额外手段将 gRPC 转换为 http 服务,让网页端也享受到其高效、低耗的好处。可是不要忘了,RPC 最经常使用的场景是 IOT 等硬件领域,网页场景也许不会在意节省几 KB 的流量。

GraphQL

GraphQL 不是 REST 的替代品,而是另外一种交互形式:前端决定后端的返回结果。

GraphQL 带来的最大好处是精简请求响应内容,不会出现冗余字段,前端能够决定后端返回什么数据。但要注意的是,前端的决定权取决于后端支持什么数据,所以 GraphQL 更像是精简了返回值的 REST,然后端接口也能够一次性定义完全部功能,而不须要逐个开发。

再次强调,相比 REST 和 gRPC,GraphQL 是由前端决定返回结果的反模式。

使用举例:

原文推荐参考 GitHub GraphQL API

好比查询某个组织下的成员,REST 风格接口多是:

curl -v https://api.github.com/orgs/:org/members

含义很明确,但问题是返回结果不明确,必须实际调试才知道。换成等价的 GraphQL 是这样的

query {
  organization(login: "github") {
    members(first: 100) {
      edges {
        node {
          name
          avatarUrl
        }
      }
    }
  }
}

返回的结果和约定的格式结构一致,且不会有多余的字段:

{
  "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"
            }
          }
        ]
      }
    }
  }
}

可是能看出来,这样作须要一个系统帮助你写 query,不少框架都提供这个功能,好比 apollo-client

Webhooks

若是说 GraphQL 颠覆了先后端交互模式,那 Webhooks 能够说是彻头彻尾的反模式了,由于其定义就是,前端不主动发送请求,彻底由后端推送。

它最适合解决轮询问题。或者说轮询就是一种妥协的行为,当后端不支持 Webhooks 模式时。

使用举例:

Webhooks 自己也能够由 REST 或者 gRPC 实现,因此就不贴代码了。举个经常使用例子,好比你的好友发了一条朋友圈,后端将这条消息推送给全部其余好友的客户端,就是 Webhooks 的典型场景。


最后做者给出的结论是,这四个场景各有不一样使用场景,没法相互替代:

  • REST:无状态的数据传输结构,适用于通用、快速迭代和标准化语义的场景。
  • gRPC:轻量的传输方式,特殊适合对性能高要求或者环境苛刻的场景,好比 IOT。
  • GraphQL: 请求者能够自定义返回格式,某些程度上能够减小先后端联调成本。
  • Webhooks: 推送服务,主要用于服务器主动更新客户端资源的场景。

3 精读

REST 并不是适用全部场景

本文给了咱们一个更大的视角看待平常开发中的接口问题,对于奋战在一线的前端同窗,接触到 90% 的接口都是非 REST 规则的 Http 接口,能真正落实 REST 的团队其实很是少。这其实暴露了一个重要问题,就是 REST 所带来的好处,在整套业务流程中到底占多大的比重?

不只接口设计方案的使用要分场景,针对某个接口方案的重要性也要再继续细分:在作一个开放接口的项目,提供 Http 接口给第三方使用,这时必须好好规划接口的语义,因此更容易让你们达成一导致用 REST 约定;而开发一个产品时,其实先后端不关心接口格式是否规范,甚至在开发内网产品时,性能和冗余都不会考虑,效率放在了第一位。因此第一点启示是,不要埋冤当前团队业务为何没有使用某个更好的接口约定,由于接口约定极可能是业务形态决定的,而不是凭空作技术对比从而决定的。

gRPC 是服务端交互的首选

前端同窗转 node 开发时,很喜欢用 Http 方式进行服务器间通信,但可能会疑惑,为何公司内部 Java 或者 C++ 写的服务都不提供 Http 方式调用,而是另一个名字。了解 gRPC 后,能够认识到这些平台都是对 RPC 方式的封装,服务器间通讯对性能和延时要求很是高,因此比较适合专门为性能优化的 gRPC 等服务。

GraphQL 须要配套

GraphQL 不是 REST 的替代品,因此不要想着团队从 Http 接口迁移到 GraphQL 就能提高 X% 的开发效率。GraphQL 方案是一种新的先后端交互约定,因此上手成本会比较高,同时,为了方便前端同窗拼 query,等于把一部分后端工做量转移给了前端,若是此时没有一个足够好用的平台快速查阅、生成、维护这些定义,开发效率可能不升反降。

总的来讲,对外开放 API 或者拥有完整配套的场景,使用 GraphQL 是比较理想的,但对于快速迭代,平台又不够成熟的团队,继续使用标准 Http 接口能够更快完成项目。

Webhooks 解决特殊场景问题

对于第三方平台验权、登录等 没有前端界面作中转的场景,或者强安全要求的支付场景等,适合用 Webhooks 作数据主动推送。说白了就是在前端无从参与,或者由于前端安全问题不适合参与时,就是 Webhooks 的场景。很显然 Webhooks 也不是 Http 的替代品,不过的确是一种新的先后端交互方式。

对于慢查询等场景,前端广泛使用轮询完成,这和 Socket 相比体验更弱,但无状态的特性反而会下降服务器负担,因此慢查询和即时通信要区分对待,用户对消息及时性的敏感程度决定了使用哪一种方案。

4 总结

最后,上面总结的内容必定还有许多疏漏,欢迎补充。

5 更多讨论

讨论地址是: 精读《REST, GraphQL, Webhooks, & gRPC 如何选型》 · Issue #102 · dt-fe/weekly

若是你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。

相关文章
相关标签/搜索