专栏首页CNCF为什么我们要改用gRPC

为什么我们要改用gRPC

作者:Levin Fritz

当你使用微服务风格的体系结构时,你需要做的一个非常基本的决定是:你的服务如何相互通信?默认的选择似乎是通过HTTP发送JSON — 使用所谓的REST API,尽管大多数人不太重视REST原则。我们在fromAtoB就是这样开始的,但最近我们决定将gRPC作为我们的标准。

gRPC是一个用于远程过程调用的系统,由谷歌开发,现在是开源的。虽然它已经存在好几年了,但是我还没有在网上找到很多关于人们为什么使用或不使用它的信息,所以我决定写一篇文章来解释我们使用gRPC的原因。

gRPC的明显优势是它使用了一种高效的二进制编码,这使得它比JSON/HTTP更快。虽然速度更快总是受欢迎的,但是有两个方面对我们来说更重要:清晰的接口规范和对流的支持。

gRPC接口规范

当你创建一个新的gRPC服务时,第一步总是在.proto文件中定义接口。下面的代码展示了它的样子 — 它是我们自己的API的一小部分的简化版本。该示例定义了单个远程过程调用“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的代码。

那么,为什么这是一件好事,而不是额外的工作?再看一下上面的代码示例。即使你从未使用过gRPC或协议缓冲区(Protocol Buffers),它非常可读的:例如,很明显,做一个Lookup请求你应该发送一个name,它是一个字string,你会得到一个Coordinate,它包含latitude和longitude。实际上,一旦你添加了一些简单的注释,例如在本例中,.proto文件就是你的服务的API文档。

当然,实际服务的规范可以更大,但不会更复杂。它只是更多的用于方法的rpc语句和用于数据类型的message语句。

由protoc生成的代码还将确保客户机或服务器发送的数据符合规范。这对调试有很大的帮助。我记得有两个实例,其中我正在处理的服务生成的JSON数据格式错误,而且由于该格式没有在任何地方进行验证,因此问题只出现在用户界面中。找出问题的唯一方法是调试JavaScript前端代码 — 如果你是一个从未使用过前端使用的JavaScript框架的后端开发者,那么调试JavaScript前端代码就不那么容易了!

Swagger/OpenAPI

原则上,使用Swagger或它的后续OpenAPI,你可以为HTTP/JSON API获得相同的好处。下面是一个与上面的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要难读得多!它更冗长,结构也更复杂(八个缩进级别而不是一个)。

使用OpenAPI规范进行验证也比使用gRPC更加困难。至少对于内部服务,这意味着要么没有编写规范,要么没有更新规范,随着API的发展,这些规范将变得毫无用处。

流处理

今年早些时候,我开始为我们的搜索设计一个新的API(想想“2019年6月1日给我从柏林到巴黎的所有连接”)。在我用HTTP和JSON构建了API的第一个版本之后,我的一个同事指出,在某些情况下,我们需要对结果进行流处理,这意味着我们应该在收到第一个结果时就开始发送它们。我的API只返回了一个JSON数组,所以服务器在收集所有结果之前不能发送任何东西。

我们在前端使用的API中所做的是让客户端轮询结果。它们发送POST请求来设置搜索,然后发送重复的GET请求来检索结果。响应包含一个字段,该字段指示搜索是否完成。这可以很好地工作,但不优雅,并且需要服务器使用诸如Redis之类的数据存储来保存中间结果。新的API将由多个较小的服务实现,我不想强迫它们都实现这个逻辑。

那时我们决定试用gRPC。要用gRPC发送远程过程调用的结果,只需在.proto文件中添加stream关键字。这是我们的Search函数的定义:

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

由protoc编译器生成的代码包括一个带有Send函数的对象,服务器代码调用该函数来逐个发送Trip对象,和一个带有Recv函数的对象,客户机代码调用该函数来检索它们。从程序员的角度来看,这比实现轮询API要容易得多。

注意事项

我想提一下gRPC的几个缺点。它们都与工具有关,而不是协议本身。

使用HTTP/JSON构建API时,可以使用curl、httpie或Postman进行简单的手工测试。gRPC也有一个类似的工具,名为grpcurl,但它并不是无缝的:你必须在服务器端添加gRPC服务器反射扩展名,或者在每个命令上指定.proto文件。我们发现在服务器中包含一个小的命令行实用程序更方便,它允许你进行简单的请求。由protoc生成的客户机代码实际上使这变得非常简单。

对我们来说,一个更大的问题是Kubernetes负载平衡器(用于HTTP服务)在gRPC上不能很好地工作。基本上,gRCP需要应用程序级的负载平衡,而不是TCP连接级的负载平衡。为了解决这个问题,我们按照本教程的指导建立了Linkerd:Kubernetes无痛作gRPC负载平衡

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

结论

尽管构建gRPC API需要更多的前期工作,但是我们发现,拥有清晰的API规范和对流的良好支持可以弥补这一点。对于我们来说,gRPC将是我们构建的任何新的内部服务的默认选项。

本文分享自微信公众号 - CNCF(lf_cncf),作者:CNCF

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-05-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • CNCF案例研究:gRPC如何实现Salesforce的统一互操作性策略

    Salesforce是客户关系管理软件领域的领导者,它的客户成功平台和其它产品支持了超过15万个组织。在幕后,“我们试图建立的一件大事是在整个公司范围内建立统一...

    CNCF
  • gRPC-Web迈向GA

    我代表云原生计算基金会,很高兴地宣布gRPC-Web的GA版本,这是一个JavaScript客户端库,使Web应用程序能够直接与后端gRPC服务通信,而不需要H...

    CNCF
  • 浏览器引入gRPC的现况

    gRPC 1.0于2016年8月发布,现已发展成为应用通信的首选技术解决方案之一。它已被全球的初创公司、企业公司和开源项目采用。它对多语言环境的支持、关注性能、...

    CNCF
  • 我们为什么从 REST 转向 gRPC

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

    Edison.Ma
  • eShopOnContainers 知多少[11]:服务间通信之gRPC

    最近翻看最新3.0 eShopOncontainers源码,发现其在架构选型中补充了 gRPC 进行服务间通信。那就索性也写一篇,作为系列的补充。

    圣杰
  • 元编程之symbol

    一些时候,写各种下划线、前后缀,为了实现一个唯一值或者秘密的特殊辅助值,用来辅助业务逻辑或者说作为一个私有的东西:

    IMWeb前端团队
  • Android开发笔记(一百二十六)自定义音乐播放器

    在Android手机上面,音频的处理比视频还要复杂,这真是出人意料。在前面的博文《Android开发笔记(五十七)录像录音与播放》中,介绍了视频/音频的录制...

    用户4464237
  • Docker实战中,从Ubuntu系列换到CentOS7.X系列应该避免的坑

    版权声明:本文为耕耘实录原创文章,各大自媒体平台同步更新。欢迎转载,转载请注明出处,谢谢

    耕耘实录
  • “捉迷藏”IoT僵尸网络,以自定义P2P形式进行传播感染的新型僵尸网络

    ? 近日,Bitdefender安全研究人员通过蜜罐系统捕获了一种物联网(IoT)僵尸网络,该僵尸网络利用自定义P2P技术进行传播控制,由于其恶意行为相对隐蔽...

    FB客服
  • 少年,你渴望元编程的力量吗?——symbol

    一些时候,写各种下划线、前后缀,为了实现一个唯一值或者秘密的特殊辅助值,用来辅助业务逻辑或者说作为一个私有的东西:

    lhyt

扫码关注云+社区

领取腾讯云代金券