说到接口的设计大部分人第一个想到的可能是REST;的确,REST是目前最为普遍的一种接口设计方式,并且作为一个优秀的接口设计标准而被广泛使用,但是除此之外,我们也不应该忘记还有其他的选项。除了REST,我们还有rpc(或者grp),最近大火GraphQL,以及webhooks. 为了好的了解这几种设计以及背后的优缺点,我们一一做简单的介绍。

  1. 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的优点也比较明显:客户端与服务器分离,简化服务器逻辑以及提高可伸缩性;无状态,降低服务器资源使用(相对于有状态的长连接),同时提高服务器的可扩展性;由于不同的信息返回时可以分开标记是否可以缓存,使得客户端可以重用之前的信息,减少客户端与服务端的交互次数。

  1. 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信息的表,伺候的请求只需要发送差异数据,避免重复的传输。
  1. GraphQL

官方定义GraphQL是一种针对API的查询语言也是 一个满足你数据查询的运行时。可以理解为另外一个种客户端与服务器之间的交互方式:前端决定后端的返回结果。它的好处是精简响应的内容,不会出现冗余字段,前端需要什么就取什么,而不需要重新定制开发api。GraphQL并不是REST的替代品,官网上有一张图描述了GraphQL与其他几种API设计的关系:

几种API设计方法REST、gRPC、GraphQL及WebHook的对比和选型

事实上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等。

  1. 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:其实从字面上理解这种应用场景就是需要服务器主动推送的地方,比如没有前端界面作为中转的服务,或者是不适合前端来操作的强安全页面(如支付),算是在特殊场景下的应用方式,毕竟相对于无状态的方式,维持连接来做推送还是开销不小,会增加不少的服务器端压力。

在技术选型上可能是一个项目开始的时候最艰难的事,不同的应用场景要选择不同的技术解决 方案,毕竟在软件开发上没有银弹,合适的才是最好的。