https://mp.weixin.qq.com/s/aEO3Y8SkObNgfQU3z8sH2w

我们为什么从REST转向gRPC

原创 Levin Fritz InfoQ 2019-06-23
作者 | Levin Fritz译者 | 无明

服务间的通信方式是在采用微服务架构时需要做出一个最基本的决策。默认的选项是通过 HTTP 发送 JSON,也就是所谓的 REST API。我们也是从 REST 开始的,但最近我们决定改用 gRPC。

gRPC 是谷歌开发的一个远程调用框架,现在已开源。尽管它已经出现了多年,但网上关于人们为什么要用它或者为什么不用它的信息并不多。于是,我决定写这篇文章分享一下我们为什么要使用 gRPC。

gPRC 的一个很明显的优势是它使用了二进制编码,所以它比 JSON/HTTP 更快。虽然说速度越快越好,但我们也要考虑另外两个因素:清晰的接口规范和对流式传输的支持。

gRPC 的接口规范

创建 gRPC 服务的第一步是在.proto 文件中定义好接口。下面的代码是一个接口的定义,它定义了一个简单的远程过程调用”Lookup“以及相应的输入和输出类型。

syntax = "proto3";
package fromatob;
// FromAtoB is a simplified version of fromAtoB’s backend API.service FromAtoB { rpc Lookup(LookupRequest) returns (Coordinate) {}}
// A LookupRequest is a request to look up the coordinates for a city by name.message LookupRequest { string name = 1;}
// A Coordinate identifies a location on Earth by latitude and longitude.message Coordinate { // Latitude is the degrees latitude of the location, in the range [-90, 90]. double latitude = 1;
// Longitude is the degrees longitude of the location, in the range [-180, 180]. double longitude = 2;}

你可以使用 protoc 编译器编译这个文件,生成客户端和服务器端代码,然后就可以开始编写调用这个 API 或提供 API 服务的代码。

那么,为什么说这个接口定义其实不算是额外的工作量反而是件好事?看看上面的代码,即使你之前从来没有使用过 gRPC 或者 Protocol Buffer,也能轻松读懂它。比如,如果要发送一个 Lookup 请求,你需要发送 name 字符串,然后会接收到由 latitude 和 longitude 组成的 Coordinate 对象。实际上,因为你已经在.proto 文件中加入了一些简单的注释,所以它也可以作为服务的 API 文档来使用。

当然,真正的服务定义规范比这个要长得多,但也不会太复杂,只是会多一些用于定义方法的 rpc 语句和一些用于定义数据类型的 message 语句。

通过 protoc 编译器生成的代码可以确保客户端发送或服务器端接收到的数据是遵循规范的,这样非常有助于调试。我记得有两次我开发的服务因为格式没有经过验证而生成了错误的 JSON 数据,这些问题只会在用户界面上表现出来。要想找出问题的根源,我们只能调试前端 JavaScript 代码,而这对于一个不太熟悉 JavaScript 框架的后端开发人员来说这并不容易!

Swagger/OpenAPI

当然,如果使用的是 JSON/HTTP,Swagger 或者 OpenAPI 也提供了类似的东西。下面的例子与上述的 gRPC API 相当。

openapi: 3.0.0
info: title: A simplified version of fromAtoB’s backend API version: '1.0'
paths: /lookup: get: description: Look up the coordinates for a city by name. parameters: - in: query name: name schema: type: string description: City name. responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/Coordinate' '404': description: Not Found content: text/plain: schema: type: string
components: schemas: Coordinate: type: object description: A Coordinate identifies a location on Earth by latitude and longitude. properties: latitude: type: number description: Latitude is the degrees latitude of the location, in the range [-90, 90]. longitude: type: number description: Longitude is the degrees longitude of the location, in the range [-180, 180].

相比 gRPC,OpenAPI 的定义更难懂,也更啰嗦,结构也更复杂(8 层的缩进)。

OpenAPI 的规范验证比 gRPC 要困难一些,至少对于内部服务来说。随着 API 的不断演化,如果不去更新规范,它就会变得毫无用处。

流式传输

今年早些时候,我开始为我们的搜索服务设计一个新的 API。在我使用 JSON/HTTP 设计了第一版 API 之后,我的一个同事告诉我说,在某些情况下,我们需要流式传输搜索结果,也就是在有第一批结果时就开始传输。而我之前设计的 API 只返回一个单独的 JSON 数组,在服务器端收集到所有结果之前是不会向客户端发送任何数据的。

我们的 API 要求客户端轮询搜索结果,先是发送一个 POST 请求发起搜索,然后再不断发送 GET 请求获取搜索结果。响应消息中包含了一个用于表示搜索是否已完成的字段。这种方式虽然没有什么问题,但还不够优雅,而且要求服务器端将中间结果保存在数据存储(如 Redis)中。

这个时候,我们决定试一试 gRPC。要通过 gRPC 发送结果,只需要在.proto 文件中加入 stream 关键字。下面是我们的 Search 函数定义:

rpc Search (SearchRequest) returns (stream Trip) {}

使用 protoc 编译器生成的代码中包含了一个对象,这个对象有一个 Send 函数,我们的服务器端代码将调用这个函数将 Trip 对象一个接一个地发送出去。代码中还包含了一个 Recv 函数,客户端代码通过调用这个函数来接收 Trip 对象。从开发者的角度来看,这比实现轮询 API 要简单得多。

注意事项

gRPC 也有一些不足之处,不过它们都与相关的开发工具有关,并不是 gRPC 本身的问题。

如果我们使用 JSON/HTTP 开发 API,就可以使用 curl、httpie 或者 Postman 进行简单的手动测试。gRPC 也有一个类似的工具叫作 grpcurl,不过它使用起来并不是很方便,你要么需要在服务器端添加 gRPC 服务器反射插件,要么需要在每个命令后面附上.proto 文件。

另一个是 Kubernetes 负载均衡器问题,负载均衡器可以支持 HTTP,但对 gPRC 支持得并不好。gPRC 要求应用层的负载均衡,而不是在 TCP 连接层。为了解决这个问题,我们参考了这篇文章搭建了 Linkerd:

https://kubernetes.io/blog/2018/11/07/grpc-load-balancing-on-kubernetes-without-tears/

结   论

尽管开发 gRPC API 在前期需要做更多的工作,但拥有清晰的 API 定义和对流式传输的支持对我们来说更重要。在构建新的内部服务时,gRPC 将会是我们的首选。

英文原文:

https://eng.fromatob.com/post/2019/05/why-were-switching-to-grpc/

为什么从REST转向gRPC 需要流式传输搜索结果,也就是在有第一批结果时就开始传输的更多相关文章

  1. 基于grpc的流式方式实现双向通讯(python)

    grpc介绍 grpc是谷歌开源的一套基于rpc实现的通讯框架(官网有更完整的定义).在搞懂grpc之前,首先要弄懂rpc是什么.下面是自己理解的rpc定义,若有不对,望指出: rpc官方称为 远程过 ...

  2. Go gRPC教程-服务端流式RPC(三)

    前言 上一篇介绍了简单模式RPC,当数据量大或者需要不断传输数据时候,我们应该使用流式RPC,它允许我们边处理边传输数据.本篇先介绍服务端流式RPC. 服务端流式RPC:客户端发送请求到服务器,拿到一 ...

  3. Go gRPC教程-客户端流式RPC(四)

    前言 上一篇介绍了服务端流式RPC,客户端发送请求到服务器,拿到一个流去读取返回的消息序列. 客户端读取返回的流的数据.本篇将介绍客户端流式RPC. 客户端流式RPC:与服务端流式RPC相反,客户端不 ...

  4. ASP.NET Core 3.0 gRPC 双向流

    目录 ASP.NET Core 3.0 使用gRPC ASP.NET Core 3.0 gRPC 双向流 ASP.NET Core 3.0 gRPC 认证授权 一.前言 在前一文 <ASP.NE ...

  5. 应答流式RPC 请求流式RPC 向流式RPC 流式RPC的三种具体形式

    https://mp.weixin.qq.com/s/pWwSfXl71GQZ3KPmAHE_dA 用Python进行gRPC接口测试(二) 大帆船 搜狗测试 2020-02-07   上期回顾:用P ...

  6. FunDA(2)- Streaming Data Operation:流式数据操作

    在上一集的讨论里我们介绍并实现了强类型返回结果行.使用强类型主要的目的是当我们把后端数据库SQL批次操作搬到内存里转变成数据流式按行操作时能更方便.准确.高效地选定数据字段.在上集讨论示范里我们用集合 ...

  7. CSS3与页面布局学习笔记(四)——页面布局大全(负边距、双飞翼、多栏、弹性、流式、瀑布流、响应式布局)

    一.负边距与浮动布局 1.1.负边距 所谓的负边距就是margin取负值的情况,如margin:-100px,margin:-100%.当一个元素与另一个元素margin取负值时将拉近距离.常见的功能 ...

  8. PHP流式上传和表单上传(美图秀秀)

    最近需要开发一个头像上传的功能,找了很多都需要授权的,后来找到了美图秀秀,功能非常好用. <?php /** * Note:for octet-stream upload * 这个是流式上传PH ...

  9. Vue2.0流式渲染中文乱码问题

    在参照vue2.0中文官方文档学习服务端渲染之流式渲染时,因为响应头默认编码类型为GBK,而文件为UFT-8类型,所以出现了中文乱码问题. 解决办法:设置响应头编码类型即可 response.setH ...

随机推荐

  1. [leetcode]118,119PascalsTriangle,杨辉三角1,2

    杨辉三角1Given numRows, generate the first numRows of Pascal's triangle.For example, given numRows = 5,R ...

  2. JDK,JRE,JVM三者之间的关系和作用

    1,定义: JDK: Java Develpment Kit java 开发工具 bin:最主要的是编译器(javac.exe) include:java和JVM交互用的头文件 lib:类库 JRE: ...

  3. std::thread线程详解(1)

    目录 目录 简介 线程的使用 线程的创建 线程的方法和属性 std::jthread (C++20) stop_token (C++20) 总结 Ref 简介 本文主要介绍了标准库中的线程部分.线程是 ...

  4. 云计算之2---KVM

    介绍 KVM KVM:就是Keyboard Video Mouse的缩写.KVM 交换机通过直接连接键盘.视频和鼠标 (KVM) 端口,让您能够访问和控制计算机.KVM 技术无需目标服务器修改软件.这 ...

  5. JS function 是函数也是对象, 浅谈原型链

    JS function 是函数也是对象, 浅谈原型链 JS 唯一支持的继承方式是通过原型链继承, 理解好原型链非常重要, 我记录下我的理解 1. 前言 new 出来的实例有 _proto_ 属性, 并 ...

  6. reactor模式:单线程的reactor模式

    reactor模式称之为响应器模式,常用于nio的网络通信框架,其服务架构图如下 不同于传统IO的串行调度方式,NIO把整个服务请求分为五个阶段 read:接收到请求,读取数据 decode:解码数据 ...

  7. .NET 云原生架构师训练营(模块二 基础巩固 MongoDB 聚合)--学习笔记

    2.5.5 MongoDB -- 聚合 排序 索引类型 创建索引 排序 // 升序 db.getCollection('author').find({}).sort({"age": ...

  8. flask从数据库反向导入Model以及出现报错No module named sqlacodegen.main

    使用flask的朋友肯定了解了flask_sqlalchemy,不了解的小伙伴也可以查看博文:Flask 操作Mysql数据库 - flask-sqlalchemy扩展 上面博文中讲解了如何将flas ...

  9. —用python写图片格式批量处理工具

    python爬取微博评论(无重复数据) 前言 一.整体思路 二.获取微博地址 1.获取ajax地址 2.解析页面中的微博地址 3.获取指定用户微博地址 三.获取主评论 四.获取子评论 1.解析子评论 ...

  10. WDCP v3 安装

    ---已更新至3.0.3---经过近期的努力,wdCP_v3正式版终于可以和大家见面了v3功能预览1 底层完全重新架构,更安全稳定,省资源更高效2 安装更简单,快速与方便3 功能更强大和易扩展,且完美 ...