项目联调阶段,约定接口的时候前后端就会坐在一起约定 http 接口,无非就是接口字段怎么传,数据格式应该怎么约定,入参是什么,出参返回数据类型是什么,怎么处理最好,对于传输方式,本文讨论的是联调前场景的选择。
- 使用的比较多的也是 Restful API 的形式进行接口设计了,他是无状态的,以资源为核心,对于操作资源约定了 URL 的带参方式,同时操作类型也是 HTTP Request 的方式,其基于 HTTP 原生接口,改造成本低,只需遵循约定的规范即可,降低了前后端耦合,利于快速迭代
curl -X GET 'https://www.baodu.com/product//list/shortList?uid=0
-
RPC 主要用于服务器之间的方法调用,影响性能的主要因素是序列化/反序列化,RPC 可打造一个高效率、低消耗的服务调用方式,适合 IOT 等对性能敏感的场景
-
gRPC 是谷歌推出的一个远程过程调用系统(Remote Procedure Call) ,基于 HTTP/2, 使用 Protocol Buffers 作为接口,其功能包括但不止于列出的部分:
- 认证(Authentication)
- 双向流(bidirectional streaming) 双工
- 流控制(flow control)
- 超时(timeouts)
-
使用场景:微服务框架下不同语言的交互, 手机浏览器等设备直连后台, 高效客户端库
-
这里主要涉及接口定义和服务器定义两块
-
-
定义接口
// the greeting service definition service Greeter { rpc SayHello (HelloRequest) return (HelloReply) {} rpc SayHelloAgain (HelloRequest) return (HelloReply) {} } message request message containing the user`s name. message HelloRequest { string name = 1; } message HelloReply { string message = 1 }
-
-
- 定义服务器
function sayHello(call, callback) { callback(null, { message: "Hello" + call.request.name }) } function sayHelloAgain(call, callback) { callback(null, { message: "Hello" + 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() }
-
- 客户端接收
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) }); }
-
客户端和服务端都需要同时拿到 protobuf 结构,客户端数据发送也要依赖 proto 提供的方法,由框架内部做序列化/反序列化的工作
-
有一些额外的手段也能将 grpc 转成 http,让网页也享受到高效低耗的好处,不过网页场景不会很在意节省几 kb 的流量
-
-
GraphQL 不是 REST 的替代品,是另一种交互形式,前端决定后端的返回结果
-
能极大程度上精简请求响应的结果,想要什么数据由前端自己决定,不过前端能决定的数据也要后端能提供支持,后端接口可以一次性定义完所有接口,而不需要逐个开发。
-
对比起来,GraphQL 只是前端决定返回结果的反模式,后端开发接口流程没有太多的变化。
-
GQL 的 API 可能看起来长这样:
curl -v https://api.github.com/orgs/:orgs/members
-
等价于 GQL query
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 完全依赖服务端主动推送,前端不主动发送请求
- 比较适合解决轮询问题,轮询其实就是一个妥协的行为,后端不支持 webhooks 模式只能依靠轮询模仿
- REST: 无状态数据传输结构,适用通用、快速迭代和标准化语义场景。
- gRpc: 轻量传输方式,适合对性能要求较高或者环境苛刻情景,IOT 等
- GraphQL: 请求自己定义数据返回格式,减少前后端联调成本
- webhooks: 服务端主动推送,适合轮询场景
- REST 不一定适用所有场景,其实像我们自己的团队,都不遵守 RESTFUl 开发方式,通常接口不做不到语义化,GET 和 SET 为主,不过接口如果要提供给第三方使用,就得好好约定接口规范语义,内部 TOB 产品,性能和冗余都比较少考虑,所以效率就是第一位,接口约定很可能是业务形态决定的,而不是凭空对技术对比得出。
- gRPC 是服务端交互的首选,像前端同学 node 开发的时候,喜欢用 Http 方式做服务器质检的通讯,可能比较疑惑为什么内部 Java 应用都不提供 Http 方式调用,而是另外的名字,了解 grpc 之后,会认识到这些平台都是对 RPC 的封装,服务间通信对性能和延时要求很高,所以适合专门为性能优化的 gRPC 服务
- GQL 需要配套使用,其不是 REST 的替代品,不要想着团队从 HTTP 接口迁移到 GQL 效率就会有 X%的提高,GQL 的方案是一种新的前后端交互约定,上手成本比较高, 同时为了方便前端同学拼接参数,等于把后端的工作量叫一部分给前端,所以如果没有一个比较好的平台查阅、维护、生成这些接口的定义,反而开发效率会下降,GQL 比较适合像美团这种大型企业有专门的团队去做基建。
- WebHooks 对于三方鉴权登录等等没有前端界面做中转的场景,或者强安全要求的支付场景,适合 Webhooks 做数据主动推送,前端不需要参与,一句话就是前端不安全,他也不是 REST 的替代品,只是一种新的前后端交互方式。对于慢查询的场景,前端轮询可以实现,反而 WebSocket 体验更佳,无状态的特性反而会降低服务器负担,所以慢查询和即时通讯要区分对待,用户对消息及时性敏感程度决定使用方案。内部对于 B 端项目版本更新的推送就使用了轮询的方案,因为版本变动的频率不高,但是也会存在变化,需要通知客户端更新。