OpenAPI 和 gRPC 一起使用

时间:2023-01-09 16:01:18

OpenAPI 和 gRPC 一起使用

花一些时间使用 OpenAPI 和 gRPC,您会发现这两种技术有很多共同点。两者都是开源成果,都描述了 API,并且都承诺为 API 生产者和消费者提供更好的体验。那么为什么我们需要两者呢?如果我们这样做,每个提供什么价值?每个项目可以从另一个项目中学到什么?

我们需要谈谈 API。

OpenAPI 和 gRPC 是构建网络 API的两种方法。引用Google API Design Guide,网络 API 是:

跨计算机网络运行的应用编程接口。它们使用包括 HTTP 和 gRPC 在内的各种网络协议进行通信,并且由不同的组织生成,通常供大于求。

最后一句话很重要:它是网络 API 的价值和挑战的关键。因为它们通常是由不同的组织生产的,所以联网的 API 使我们能够与世界上任何地方的人一起工作,并且它们使我们能够做出比我们任何人单独创造的更大的东西……但我们必须能够就我们正在做的事情相互交谈。我们需要一种描述 API 的语言。

OpenAPI 是一种用于描述 REST API 的语言。

如果您使用 REST API,您可能听说过OpenAPI 规范或其前身 Swagger。它是一种使用 JSON 或 YAML 描述 REST API 的结构化方式,最初是为了更好地记录 API 而创建的。但人们很快看到了它的其他用途——API 验证、代码生成、模拟、测试——许多很棒的应用程序可以更轻松地创建和使用 API。现在 OpenAPI 规范由 Open API Initiative 拥有和管理,Open API Initiative是 Linux 基金会的一个项目,其创始成员包括 Google 和 Apigee,Apigee 是 Google 于 2016 年收购的 API 管理平台供应商。它由技术领导团队管理,并得到超过两打 Open API Initiative 成员公司的支持.

OpenAPI 规范不是第一个 API 描述格式,但迄今为止它是使用最广泛的。在我们拥有 API 描述格式之前,人们为他们的 API 手工编写代码。然后他们手写了这些 API 的描述,并将其交给想要使用这些 API 的人,然后由他们手写代码来调用这些 API。所有这些手写导致了很多变化和错误。作为一种正式的描述格式,OpenAPI 为我们提供了一种很好的方式来交流 API,并在我们基于 API 的系统中减少错误并取得更多成功。

gRPC 源于 Google 构建 API 的方式。

我们正在讨论的另一个项目是gRPC。gRPC 最初由 Google 开发,是一个用于制作 API 的开源系统,它基于 Google 内部构建 API 的方式。在 Google 内部,gRPC 用于描述从设计到实施和部署的 API。API 文档是从 gRPC API 描述生成的。gRPC 包括自动生成 API 客户端的代码生成器和可以填充以创建 API 服务器的脚手架。然后,gRPC API 描述被 API 测试系统、API 代理和各种提供身份验证、配额、计费和安全等服务的 API 支持系统使用。

gRPC 现在属于Cloud Native Computing Foundation,已经被谷歌以外的许多团体和公司采用。因为 gRPC 是谷歌内部一直使用的公共版本,所以谷歌也在向 gRPC 转换。gRPC 的首批用户中有两个是移动应用程序GBoardAllo,随后还有更多用户。

API方法论,你听说过吗?

在设计自动化软件方面的工作中,制作芯片设计工具,但仅仅编写工具是远远不够的。每个工具都必须符合一种方法论、一套工具和做某事的最佳实践——在我们的例子中,就是芯片设计。芯片设计工具可能会为芯片上的单元分配位置、布置这些单元之间的连接,或检查设计是否存在性能问题。但这是分多个阶段完成的,每件事都必须在流程中的正确时间点完成。开发和管理该过程的人称为方法学家

无论我们是否意识到,我们也在使用一种方法来构建我们的 API 系统。我们可能首先编写代码,也许从原型或模拟服务器开始,然后编写文档或构建 OpenAPI 描述。在某些时候,我们可能会编写一些测试并设置一个持续集成系统来构建和验证我们的 API 实现。我们可能会遵循 API 风格指南,并且我们工作的某些部分可能会自动化。所有这些都是我们 API 方法的一部分。

OpenAPI 深入。

由于它的流行,您可能已经听说过一些关于 OpenAPI 或其前身 Swagger 的事情。以下是 OpenAPI 的一些最重要的特性:

OpenAPI 提供了一种 API 描述格式。

OpenAPI 规范提供了一种使用 JSON 或 YAML 文档描述 REST API 的方法。文档遵循由 JSON 模式和文本规范描述的标准形式(具有讽刺意味的是,文本文档是 OpenAPI 规范的官方形式,它试图用机器可读的 OpenAPI 描述替换 API 的文本定义)。

网上有很多示例 OpenAPI 描述。您可以在apis.guru/openapi-directory找到一个大型档案,许多公司和项目发布和托管他们发布的 API 的 OpenAPI 描述。例如,Kubernetes项目在其主要 Git 存储库中包含Kubernetes API 的 OpenAPI 描述。不幸的是,没有标准的地方可以查找 API 的 OpenAPI 描述,但通常可以在名为 openapi.json 和 openapi.yaml(或旧版本的 swagger.json 和 swagger.yaml)的文件中找到描述。

建立在 JSON 和 YAML 之上使得 OpenAPI 对人类和机器具有同等的可访问性,也许是通过对两者做出一些妥协。这些是文本格式,因此人们可以阅读 OpenAPI 描述,但有时作者会被 JSON 中过多的标点符号或 YAML 的缩进要求绊倒。从机器的角度来看,用大多数编程语言导入 YAML 或 JSON 文件真的很容易。但是在强类型语言中——将 JSON 或 YAML 文件导入通用数据结构然后必须将这些通用数据结构转换为它们的类型化组件可能很尴尬。在这样的环境中,为您的 API 描述提供强类型数据结构会很有帮助。这仍然可以通过 OpenAPI 实现,但不是规范或官方工具的一部分。

OpenAPI 假定使用 HTTP 进行传输。

Open API Initiative 的目标是标准化“REST API 的描述方式”,因此通常假定 HTTP 是 OpenAPI 规范描述的 API 的传输层。这意味着 API 请求是事务性的:发送请求并接收响应。因为它专注于 REST API,所以 OpenAPI 根据路径和 HTTP 动词(如 GET、POST、PUT、DELETE)来描述 API。

OpenAPI(通常)假定 JSON 用于数据表示。

OpenAPI 提供不同的响应类型,但对于几乎所有实际用途,结构化消息都作为 JSON 文档发送和接收。

OpenAPI 使代码生成成为可能。

正式地,Open API Initiative 只管理规范;没有官方的 OpenAPI 工具。但作为方法论的一部分,当您开始考虑围绕 API 描述格式还需要什么时,您不想手动编写 API 支持代码。它乏味、容易出错且昂贵,没有人愿意拥有和维护它。所以在 OpenAPI 世界中有用于生成代码的工具。通常他们首先生成客户端库,即调用 API 所需的代码,但您也可以生成支持代码的服务器端实现,以帮助您发布 API。最大的 OpenAPI 代码生成项目称为swagger-codegen,但还有其他几种代码生成器可用,包括AutoRestMicrosoft 的项目,Capital One 的一个名为oas-nodegen的项目,以及来自APIMATIC的支持 OpenAPI 和其他格式的商业代码生成系统。肯定还会有更多。

许多 API 发布者已经在使用 swagger-codegen 和类似工具来生成他们提供给用户的 SDK。LyftSquare的博客文章描述了他们如何使用 swagger-codegen 生成他们的客户端库。但他们并不总是开箱即用地使用 swagger-codegen:通常,API 生产者想要做的特殊事情需要对代码生成进行一些定制。

生成客户端库涉及自定义提供的特定于语言的模板…… 每个模板需要的修改量因语言而异,我们期待与 Swagger Codegen 社区合作,分享我们的改进。 ……Swagger Codegen 是一个非常活跃的项目。如果您不签入您的模板,事情将会意外中断,因为 Swagger Codegen 只使用现有的最新和最好的模板。因此,如果你不手动确保这些东西正常工作,你就会在那里遇到问题。

因此,作为发布 SDK 的 Lyft 和 Square 方法的一部分,他们实际上已经分叉了一个用于生成 API 的 swagger-codegen 版本。这可能具有挑战性,因为 swagger-codegen 是一个大项目。它至少有 70 个不同的目标,所有这些目标的质量都不同。

整体代码生成很难。

这是我们在谷歌也看到的问题。如果您有一个可能包含多个目标的代码生成器,那么可能会面临许多管理、开发和文化方面的挑战,这些挑战可能会促使您将其分解成多个部分。

  • 生成器构建时间长:更改一个目标可能需要重建所有内容。
  • 生成器测试时间长:新构建通常针对每种目标语言进行测试,即使只更改了一个目标也是如此。
  • 为了稳定性,团队可能更愿意存档他们自己的生成器构建。如前所述,像 Square 的 SDK 团队这样的团队可能希望将自己的生成器放在一边,以便其他对 master 分支的提交不会在 Square 生成的客户端库中引入意外。
  • 分叉将比比皆是,每个分叉都会分叉整个生成器和所有目标。
  • 质量参差不齐。一些语言目标已经成熟且经过充分测试,而另一些则勉强进入“爱好项目”阶段。
  • 版本控制很难。通常生成器会有一个版本,但语言目标本身没有版本,因此很难判断新版本何时会包含对特定语言目标的重大更改。
  • 复杂性和可能不熟悉的构建系统阻止了可能是生成语言专家但不熟悉用于编写生成器的语言的贡献者。

OpenAPI 在开发人员所在的地方与他们会面。

但是当你退后一步看 OpenAPI 时,你会发现一个非常擅长与开发人员会面的项目。很多人都在使用 JSON 和 REST 来构建他们的 API,而 OpenAPI 团队基本上已经找到他们并说,“你在做什么?这是它的描述。那对你有用吗?” 这种倾听和改进催生了 OpenAPI 3.0,现在我们有了一个可以描述世界上大多数人如何制作 REST API 的规范。

gRPC 深入。

另一方面,gRPC 源于谷歌的扩展努力。随着 Google 的发展以及越来越多的组件被实现为微服务,降低大规模构建和运营这些微服务的成本变得很重要。

您可能会惊讶地发现 Google 内部几乎没有使用 REST API。没错,绝大多数 Google 的内部微服务都不使用 REST。相反,几乎所有东西都使用 gRPC 或称为“Stubby”的 gRPC 前身进行通信。

gRPC 使用 HTTP/2 进行传输。

gRPC 与其内部前身的不同之处之一是它通过 HTTP/2 进行通信。在谷歌内部,上一代基于 Protocol Buffer 的 API——我们接下来将讨论 Protocol Buffers——只会打开一个套接字,写入一条消息,然后读取一条消息回复。转向 HTTP/2 允许流式 API:当您打开 HTTP/2 连接时,连接可以在多个调用中保持打开状态,并且这些调用可以同时发生。您可以获得通过单个持续连接多路复用多个调用的效率,并且您还可以在这些持续连接上操作流式 API。使用 gRPC,我可以调用一个 API 来返回股票报价流,或者调用另一个 API 来获取音频样本流并返回对我所说的话的解释流。

gRPC 使用 Protocol Buffers 进行数据表示。

在较低的层次上,gRPC 可以被认为只是一个传输系统:一种通过 HTTP/2 进行 API 调用并支持可选流的协议。但更常见的是,gRPC 被认为是一种方法论,在 gRPC 方法论中,消息是用Protocol Buffers编码的。

Protocol Buffers 被描述为“一种用于序列化结构化数据的语言中立、平台中立的可扩展机制”。从这个意义上说,Protocol Buffers 也是一种方法论。在这里,我使用“Protocol Buffers”(大写)来描述方法,使用“protocol buffer”(小写)来描述数据结构的序列化。

协议缓冲区(序列化)是一个字节数组,表示一个复杂且通常是分层的数据结构。编码由一些简单的规则描述。它们非常简单,编码器或解码器可以在一两天内编写完成。编码是一种二进制格式,因此人类很难阅读,但它很容易被机器编写和快速读取。尽管任何比较都依赖于结构,但协议缓冲区的读取速度可能比相应的 JSON 序列化快几个数量级。

几乎所有在 Google 内部传递的东西都使用 Protocol Buffers 表示。没有内部 REST API,也没有 JSON 或 XML。相反,谷歌内部使用的服务和微服务接收和发送协议缓冲区,据估计,在所有谷歌数据中心,每秒进行超过百亿次 API 调用。这种使用量是使用协议缓冲区的原因,也是协议缓冲区编码已针对快速写入和读取进行优化的原因。

gRPC 提供了 API 描述格式。

protocol buffer 中编码的数据不是自描述的:要反序列化 protocol buffer,读者需要消息中字段的描述,特别是它们的类型和字段编号(消息中的每个字段都被赋予一个唯一的编号,即在编码中使用)。

这些描述是用一种叫做Protocol Buffer Language 的特殊语言编写的。因为它描述了一个 API,所以这类似于 OpenAPI 规范,但我们有一些看起来更像典型编程语言的东西,而不是 JSON API 描述。它有一个语法,可以为它编写解析器,还有一个标准的编译器。编译器称为protoc,是“协议编译器”的缩写。

这是服务的一个非常简单的协议缓冲区语言描述:

package echo;
message EchoRequest {
  string text = 1;
}
message EchoResponse {
  string text = 1;
}
service Echo {
  rpc Get(EchoRequest) returns (EchoResponse) {}
  rpc Update(stream EchoRequest) returns (stream EchoResponse) {}
}

该服务称为Echo,因为它只是将接收到的内容发回。我在这里多余地定义了两条消息,EchoRequestEchoResponse。因为它们具有相同的内容,所以我们可以只使用一个。在这些消息声明之后,有一个服务声明说:“我们将拥有一个 API。我们称之为Echo。它支持两个远程过程调用——一个称为Get接受EchoRequest并返回一个EchoResponse,另一个称为Update接受EchoRequest消息流并返回EchoResponse流消息。” 这是 gRPC 可以实现的一种流式 API。

在 gRPC 中,代码生成是标准做法。

当您使用protoc编译 API 的 Protocol Buffer 语言描述并生成 API 支持代码时,这将成为方法论。如果您只想运行protoc并获取其输出,您可以提供**-o选项,protoc将编写一个描述您的 API 的二进制文件。该二进制文件称为FileDescriptorSet**,它由Protocol Buffer 语言文件描述。Protocol Buffers 有很多用途!但可能更有用的是:如果您想获得特定语言的 API 支持代码,您可以使用其中一个 gRPC协议插件,如下所示:

protoc echo.proto -o echo.out --swift_out=.

在这种情况下,我需要一些 Swift 代码来实现Echo协议缓冲区,所以我使用了**--swift_out选项。按照惯例,这会调用一个名为protoc-gen-swift的插件。当您运行protoc时,这需要在您的路径中。完成此操作后,您会得到一个名为echo.pb.swift的文件**其中包含为您的消息生成的代码。该代码包括一个 Swift 数据结构来表示您的消息,并包括支持代码将该结构转换为序列化数据并将该序列化数据转换回您可以在 Swift 程序中使用的结构。这样一来,作为开发人员,您无需了解 Protocol Buffers 的二进制格式。您可能只是为了好玩或编写自己的 Protocol Buffer 库而想知道它,但您不需要知道它就可以使用 Protocol Buffers。您可以只使用此生成的代码。

还有一件事:protoc是一个 C++ 程序,而protoc-gen-swift是使用 Swift 编写的。一般来说,插件可以用任何支持 Protocol Buffers 的语言编写,因为protoc插件接口本身是由Protocol Buffer 语言文件定义的。

gRPC 支持分布式代码生成。

protoc插件架构带来了许多优势:

  • 快速构建时间:改变一个目标只需要重建它的插件。
  • 快速测试时间:新构建只需要针对受影响的目标进行测试。
  • 为了稳定性,团队可以存档他们自己的协议和插件构建。
  • 新插件比比皆是。
  • 单独维护的插件可以提供不同的成熟度级别。
  • 单独维护的插件可以进行适当的版本控制。
  • 单独维护的插件可以用贡献者喜欢的语言编写。

您可能已经注意到,这是对我们在单体代码生成器中看到的问题的逐点响应。

所以如果我想改变 Swift 代码生成器,我不必去碰 Go 代码生成器。那是在一个单独的 Git 存储库中,我不必测试它,因为我确定我没有碰过它。如果我想分叉一个语言生成器,我还没有分叉整个世界。

目前,gRPC 正式支持 10 种语言,并会支持更多语言。因为 gRPC 使用 Protocol Buffers 进行通信,所以客户端和服务器可以用这些语言中的任何一种编写并*互操作。当 API 使用 gRPC 时,没有客户端或服务器需要有关用于实现其他客户端或服务器的编程语言的任何信息。

构建 gRPC 服务,获取免费的 REST API。

gRPC 的结构化、描述驱动的 API 方法还有另一个好处。如果你构建一个 gRPC 服务,你可以使用像 grpc-gateway这样的第三方项目来免费获得一个 JSON/REST API。在 API 描述中使用特殊注释,grpc-gateway 将 gRPC API 转码为具有 JSON 消息编码的 REST API。这些注释记录在google/api/http.proto中,因为这是 Google 编写的 Protocol Buffer 语言文件,您可能会(正确地)猜到 Google 的 REST API 是以非常相似的方式实现的。

gRPC 力求最高的性能和质量。

退一步看看 gRPC。像我一样,我认为您会看到一个项目为开发人员提供一种方法,该方法致力于为 API 提供最高的性能和质量。

两个项目,API 空间的两个角落。

当我们将这两个项目放在一起时,我们会发现每个项目都在 API 空间的不同角落表现出色。

OpenAPI通过 API 描述格式满足开发人员的需求,该格式可用于创建可靠的方法来生成他们今天生成的 JSON/REST API。gRPC 专注于通过要求开发人员遵循严格、定义明确的方法来提供强大的流式 API 构造和高性能实现,从而为 API 提供最高的性能和质量。

这些项目可以从其他项目中学到什么?让我们从 OpenAPI 开始:

  • 正如我提到的,OpenAPI 是一种使用 YAML 和 JSON 的非常动态的 API 描述格式。使用 gRPC,当我编译 API 描述时,我得到一个描述我的 API 的强类型数据结构。因此,我们可以改进 OpenAPI 的一种方法是构建类似的 OpenAPI 描述的强类型表示,以便在我们的 API 支持工具中使用。
  • 我们可以通过构建更多分布式、面向插件的代码生成器来提高 OpenAPI 空间中代码生成的质量,这些代码生成器可以更轻松地在生产中使用代码生成并使用它们生成的相同语言编写代码生成器。
  • 最后,我们可以通过从考虑 API 描述格式到考虑完整的 API 方法来进一步扩大 OpenAPI。我们的 API 生命周期有哪些部分?让我们使用基于 OpenAPI 的工具来解决所有这些问题,并将我们的工具组织成以一致、定义明确的方式生成 API 的方法。

在它的角落里,gRPC 可以从 OpenAPI 中学到一些东西:

  • 开始使用 gRPC 可能很难。更多用户友好的工具可能会有所帮助,例如与curl命令等效的工具,用于在命令行发出 HTTP 请求。
  • 正如 OpenAPI 表示可能过于动态一样,gRPC API 表示也可能过于静态。出于性能原因,大多数 gRPC 实现严重依赖代码生成,但当性能不是关键时,动态 gRPC 库可以更轻松地快速编写应用程序,尤其是 API 客户端。
  • 开始为 gRPC 编写工具也很难。大多数程序员都知道如何读写 JSON 和 YAML,但是使用 Protocol Buffers 有点困难。更多简单的示例应用程序和教程将使 gRPC 成为一种更易于访问的方法。

你能提供什么帮助。

OpenAPI 和 gRPC 一起使用

您会在 API 世界中发现许多不同的观点;其中一些在 OpenAPI 和 gRPC 背后的设计和价值中显而易见。但为了达成共识,我建议您做以下六件事来帮助我们向前迈进:

首先,停止为您的 API 手动编写接口代码。

它很容易出错,并且可能会引入我们上次手动编写 API 客户端和服务器时引入的相同错误。

其次,停止为您的 API 手动编写接口代码。

当我们手工编写界面代码时,我们鼓励不必要的变化,随着时间的推移,变化会助长复杂性和成本的增长。如果我们在许多不同的地方做同样的事情,我们应该用相同的代码来做,无论是在公共库中还是在代码生成器生成的一致代码中。

第三,停止为您的 API 手动编写接口代码。

严重地。手写界面代码编写成本高,维护繁琐,从长远来看,没有人愿意拥有它。

第四,注意你的 API 方法。

注意你如何制作 API 的整个过程。更好的是,将其记录下来并安排专人负责监督。

第五,制定和使用 API 风格指南。

谷歌有一个API 风格指南并虔诚地使用它。我认为每个公司都应该使用一个,无论您是编写自己的指南还是使用别人的指南。这就像使用编程语言的风格指南:有时程序员会对这些感到愤怒,但我们需要它们,所以如果您使用语言风格指南,请考虑使用 API 风格指南。与此同时,考虑在您的公司中设立一个审查部门,使用您的风格指南来审查所有 API 的一致性。提出这样的问题:“我们是否对同一件事使用相同的术语,或者我们是否会让我们的每个团队以自己独特的方式做事来混淆我们的用户?”

最后,使用 OpenAPI 或 gRPC。

这两个项目在提高 API 空间的连贯性和一致性方面取得了最大进展,而且它们都是开源的。您可以在不贡献的情况下使用它们,但如果您需要改进,那就更好了!对贡献者的唯一限制是贡献者许可协议和行为准则。

如果你喜欢我的文章,点赞,关注,转发!