vlambda博客
学习文章列表

gRPC系列:什么是gRPC API,它如何工作?

本文翻译Bob Reselman的发表的文章,译者能力有限,翻译不当处请见谅。

gRPC已成为实现需要大规模快速运行的分布式软件系统的一项重要技术。简而言之,gRPC是一种API框架,它允许Internet上一个位置的程序将数据传递给Internet上另一个位置的另一个程序的不同功能进行处理。尽管其他API框架(如REST)通常使用基于文本的格式(如JSON或XML)将数据从客户端传递到服务器,然后再传递回,但在gRPC下,数据以二进制格式在客户端和服务器端目标函数之间传递。gRPC的有效负载的二进制性质是其声誉比其他方法更快的原因之一。使用gRPC的程序可以在十亿分之一秒内执行,而使用基于文本的数据时通常需要几毫秒的时间。

自从规范于2015年2月由Google开源发布以来,gRPC就引起了开发社区的极大兴趣。(请参见图1。)

图1:根据Google趋势,自2015年发布以来,对gRPC的兴趣不断增长

大小公司都在使用gRPC,其中包括Slack,Microsoft,CondéNast,Netflix和Cisco等著名品牌。

我们ProgrammableWeb相信gRPC是IT领域的永久性工具。实际上,考虑到上述趋势,有充分理由认为该技术的采用将继续增长,特别是在那些需要快速的系统来满足其关键任务需求的企业中。

对于那些正在或计划在Webscale上进行企业级分布式系统开发的公司,在诸如REST和GraphQL之类的流行替代方案的背景下对gRPC具有广泛的了解至关重要。提出对gRPC的这种理解是本文以及本系列后面的其他文章的目的。

在本文中,我们将讨论gRPC如何在分布式计算领域出现。此外,我们还将概述gRPC规范,并展示如何使用演示性gRPC API实施该规范。专为这一系列文章而创建。从提供对包含客户端和服务器组件的复杂演示gRPC应用程序的详细说明,到一系列深入的访谈(包括一系列内容),本系列的后续文章将详细介绍gRPC的各个方面。实施gRPC作为其技术堆栈的一部分的公司。此外,我们还将研究大规模使用gRPC的问题和解决方案。我们将介绍很多内容。但是,与任何旅程一样,我们需要从头开始,并且从了解gRPC的需求和历史开始。本质上,gRPC的遗产就是在计算机网络之间分配离散功能

进程间通信的需求

自从数字技术开始以来,就一直存在共享计算资源的需求。数据共享是起点。公司需要将数据从一台计算机移动到另一台计算机,以便以特定于每个系统的方式处理信息。例如,一家银行与另一家想要确定信用度的银行共享客户的贷款历史信息并不罕见。

但是,就最大限度地提高计算效率而言,简单地共享数据的用途有限。确实,通过共享数据可以节省时间,但是每台计算机都必须使用自己的算法来处理这些信息。很多时候,一个单一的算法在许多机器之间被复制。这种冗余效率很低。如果更新了算法,则该更新需要在使用该算法的所有计算机之间传播。这是一项冒险的工作。因此,使单个计算机有可能与其他计算机共享其算法(也称为过程)的概念发展了起来。过程的一对多共享被称为远程过程调用(RPC)。

远程过程调用,gRPC的前身

RPC的基本思想是,在一台计算机上运行的过程(也称为函数)可以由网络上不同位置的许多其他计算机共享。(请参见下面的图2)

gRPC系列:什么是gRPC API,它如何工作?

图2:RPC使计算机可以与其他计算机共享过程(功能)

RPC的实现

如上所述,RPC已经存在了一段时间。早在1970年代末到1980年代末期的Unix编程中就可以找到它的踪迹。同样,从概念上讲,RPC间接出现在许多技术中。例如,存储过程技术是在数据库中嵌入函数的一种常用方法,其根源于RPC思想。

存储过程

与关系数据库管理系统(RDBMS)一样,总是提到存储过程。通常的想法是,用于更新数据库的可重用业务逻辑始终保持在数据库本身的大致范围内(通常在同一RDBMS中)。存储过程的工作方式是,不是让客户端程序准备要存储的数据,然后执行必要的INSERT或UPDATE SQL查询以将该数据存储在数据库中,而是将插入或更新的逻辑存储为命名函数。在数据库系统本身中。下面的清单1显示了两个存储过程的示例。

USE MyDatabase

GO
-- the stored procedure that validates data
CREATE PROCEDURE [dbo].[usp_customer_validate]
 @first_name varchar(75),@last_name varchar(75), _
      @gender varchar(6),@email varchar(75), _
 @postal_code varchar(12)
AS
BEGIN
      -- the validation code goes here
END

GO

-- the stored procedure that inserts data
CREATE PROCEDURE [dbo].[usp_customer_insert]
 @first_name varchar(75),@last_name varchar(75), _
      @gender varchar(6),@email varchar(75), _
 @postal_code varchar(12)
AS
BEGIN 
      -- use the stored procedure usp_customer_validate
      EXEC usp_customer_validate @first_name, @last_name, @gender, @postal_code
 
      -- insert the data
      INSERT INTO customers (first_name,last_name,gender,email,postal_code)
 VALUES  (@first_name,@last_name,@gender,@email,@postal_code)
END

GO

清单1:在基于SQL的数据库服务器(例如Oracle或Microsoft的服务器)上运行的存储过程的示例

一个存储过程名为usp_customer_validate。另一个名为usp_customer_update。存储过程usp_customer_validate验证作为过程参数传入的客户信息。存储过程usp_customer_insert使用usp_customer_validate存储过程来验证提交的客户数据。然后,usp_customer_insert将一条记录插入名为customers的表中。表客户是一个名为MyDatabase的虚拟数据库的一部分。

当存储过程正在运行时,客户端只需要建立与数据库的网络连接并将原始数据传递到数据库中给定的存储过程中,便无需做任何其他事情。下面的清单2是一个示例,显示了如何使用.NET / C#调用存储过程。

var connectionString = "Server=(local);DataBase=MyDatabase;Integrated Security=SSPI";
using (SqlConnection sqlConnection1 = new SqlConnection(connectionString)) {
using (SqlCommand cmd = new SqlCommand()) {
  Int32 rowsAffected;

  cmd.CommandText = "usp_customer_insert";
  cmd.CommandType = CommandType.StoredProcedure;
  
  cmd.Parameters.Add (new SqlParameter ("@first_name""Dick"));
  cmd.Parameters.Add (new SqlParameter ("@last_name""Tracy"));
  cmd.Parameters.Add (new SqlParameter ("@gender""male"));
  cmd.Parameters.Add (new SqlParameter ("@email""[email protected]"));
  cmd.Parameters.Add (new SqlParameter ("@postal_code""02122"));
  cmd.Connection = sqlConnection1;

  sqlConnection1.Open();

  rowsAffected = cmd.ExecuteNonQuery();
}}

清单2:使用存储过程将数据插入基于SQL的数据库中的C#客户端代码

然后,存储过程完成验证和存储数据的工作。数据验证和存储对客户端是不透明的。所有这些工作都在数据库内部完成。如果需要更改数据库的架构,则在这种情况下,将更新存储过程内部的SQL查询。客户的行为完全没有改变。这本质上是RPC的动力。该功能存在于网络中的一个位置,并由位于其他位置的客户端调用。

RPC和ERP

RPC还存在于企业资源计划(ERP)系统中。SAP是一项主要的ERP技术,支持名为“远程功能调用(RFC)”的功能。RFC本质上是用于SAP业务系统的RPC。(请参见下面的图3。)

gRPC系列:什么是gRPC API,它如何工作?

图3:SAP的远程功能调用(RFC)是RPC体系结构样式的变体

Java下的RPC

今天,RPC的最常见实现之一是使用一种称为Java远程方法调用(Java RMI)的技术。Java RMI使得任何启用Java虚拟机的计算机(JVM)都可以将Java函数公开给运行Java虚拟机以及Java RMI库的其他计算机。

Java RMI本质上是一种客户端-服务端体系结构,但略有改动。客户端需要获得对具有特定远程方法的RMI服务器的引用,而不是像在Internet上典型的RESTful API请求/响应交互中那样直接调用端点。(请参见下面的图4)。甚至在进行对话之前,还需要进行很多往返操作。

gRPC系列:什么是gRPC API,它如何工作?

图4:Java远程方法调用使访问远程服务器上的功能成为可能

下面的清单3显示了Java类MyClient的代码,上面的图4中说明了其中的一部分。类MyClient完成了与Java RMI服务器进行交互的工作。

gRPC系列:什么是gRPC API,它如何工作?

清单3:调用远程方法的Java RMI客户端代码

客户端代码的工作方式是通过在服务器计算机上运行的Remote Object Registry上进行查找来获得对远程功能的引用,如清单3的第8行所示。一旦建立了到远程服务器的连接,然后从Remote Object Registry获得一个引用,该引用用于使用用户在第14行提供的输入数据在服务器上调用远程方法(清单3,第18行)。然后将调用结果传递回给用户在第21行

上手Java RMI教程

参加有关Katacoda的ProgrammableWeb交互式教程,以获取使用Java代码的实践经验,该代码演示了远程方法调用(RMI)的基础。您可以在https://katacoda.com/programmableweb/scenarios/understanding-java-rmi上找到该教程。

gRPC系列:什么是gRPC API,它如何工作?

正如您甚至从上面显示的简单示例中看到的那样,Java RMI是一项强大的技术,可以使分布式计算更加高效。但是,它有一个根本的缺点。使用Java RMI时,客户端和服务器都必须使用Java。例如,您不能轻易让.NET代码直接调用Java RMI代码。对于.NET,有一些变通办法,例如JNBridge。但是,整个Java RMI旨在成为Java解决方案。Polyglot开发支持不是产品设计的一部分。

尽管如此,尽管缺乏对多语言开发的支持,Java RMI还是将RPC引入了用于商品硬件环境的分布式计算的主流,并为包括gRPC在内的下一代RPC奠定了很多基础。

但是,尽管Java RMI对基于RPC的API开发产生了积极的影响,但是对多语言开发的支持仍然是一个迫切的需求,特别是随着PHP,Python和.NET在开发领域的普及。XML-RPC满足了这一需求。

XML-RPC

顾名思义,XML-RPC是一种基于可扩展标记语言(XML)的远程过程调用协议。XML-RPC是与语言无关的规范。任何可以处理XML的编程语言都可以支持XML-RPC。

XML-RPC已经存在了一段时间。该规范于1998年首次发布。尽管近年来对XML-RPC的兴趣在减弱,但该协议仍在使用中。WordPress使用XML-RPC启用从外部客户端在手机和平板电脑上的发布。此外,Atlassian在其Confluence产品中提供了XML-RPC接口。(有关支持XML-RPC的API的完整列表,请在此处找到的ProgrammableWeb API目录中搜索xml-rpc一词。)

简而言之,XML-RPC客户端使用标准HTTP将数据发送到驻留在XML-RPC服务器上的预定义函数。数据交换的通用语言是XML,该XML根据XML-RPC规范定义的标准进行了格式化。如上所述,XML-RPC与语言无关,因此完全可以想象,当服务器使用Java编程时,客户端程序可以用C编写。这种灵活性使XML-RPC成为特定语言的RPC框架(如Java RMI)的可行替代方案,并且对于许多人而言,这是朝着分离客户端和服务器之间的关注迈出的一大步。API革命的重要宗旨。

但是,灵活性需要付出一定的代价。XML是基于文本的格式。因此,相对于二进制方法,在数据交换中来回发送的数据非常庞大。尽管某些XML-RPC API服务器可以接受诸如.zip或.rar等压缩格式的数据,但XML-RPC规范将请求和响应的内容类型描述为text / xml。对于需要以纳秒量级的速度交换数据的应用程序来说,这可能是一个障碍。但是,对于允许在毫秒级进行交换的应用程序,XML-RPC仍然是可行的API框架,这就是为什么某些API继续依赖它的原因。

通过运行简单XML-RPC演示项目,获得直接使用XML-RPC的经验。

所有下面所示的例子可以使用上找到的简单的XML-RPC示范工程运行ProgrammableWeb的GitHub的仓库在这里。此外,您可以在此处通过在Katcoda上学习ProgrammableWeb上有关XML-RPC的交互式课程来直接使用Simple XML-RPC 。

gRPC系列:什么是gRPC API,它如何工作?

使用XML-RPC遵循与任何HTTP数据交换中相同的请求/响应模式。这种交换的详细信息将在以下各节中介绍。

XML-RPC请求剖析

下面的清单4显示了请求的XML示例,该请求调用了一个名为“ ping”的过程,该过程位于承载XML-RPC API的同一服务器上。

gRPC系列:什么是gRPC API,它如何工作?

清单4:对演示XML-RPC API服务器上的ping方法的简单调用

与REST不同,在REST中,API公开的唯一功能是隐式HTTP动词(例如GET,POST,PUT和DELETE),而对XML-RPC API的调用所针对的功能/过程的名称直接编码为XML结构发送到API服务器。该函数附带的参数也被编码。

在上面的清单4中,如第2行所示,标记<methodCall>是XML元素,该元素定义了在XML-RPC API上调用的方法。XML-RPC规范要求此<methodCall>标记。清单4第4行的<params>标记是一个元素,它将包含要发送到API的参数以及过程名称。每个<params>元素将包含一个或多个<param>元素。每个<param>都有一个<value>子元素,如清单4第6行所示。<value>的内容元素将根据值的标量类型而变化。在清单4上方的情况下,第7行的参数值的标量类型是一个字符串。因此,我喜欢冰淇淋的字符串被封装在一组<string> </ string>元素中。

下表1显示了XML-RPC规范中定义的六种缩放器类型。

Tag Type Example
<i4> or <int> four-byte signed integer -22
<boolean> 0 (false) or 1 (true) 1
<string> string I like ice cream
<double> double-precision signed floating point number -22.214
<dateTime.iso8601> date/time 19980717T14:08:55
<base64> base64-encoded binary eW91IGNhbid0IHJlYWQgdGhpcyE=

表1:XML-RPC标量类型

如上所述,XML-RPC数据交换遵循HTTP请求/响应模式。客户端将XML作为HTTP POST发送到API的端点。通常,一台XML-RPC服务器上只有一个端点。本文随附的Simple XML-RPC Server演示项目在HTTP根目录/侦听传入的XML-RPC请求。

服务器接受请求并对其进行验证,以确保请求标头和正文中的XML格式正确。然后将请求传递到服务器内部进行反序列化和处理。

重要说明:XML-RPC HTTP请求的标头中必须包含Content-Length!

XML-RPC规范要求对XML-RPC API的所有HTTP请求都在请求标头中包含Content-Length属性。Content-Length必须以字节为单位指定请求正文中XML的大小。一旦处理成功,结果将被序列化为XML,并通过HTTP响应发送回客户端。让我们看一下响应的细节。

XML-RPC响应剖析

下面的清单5显示了一个HTTP响应中从XML-RPC API过程接收到的XML的示例,如上所述,该过程名为ping。XML是根据XML-RPC规范中描述的格式构造的。

gRPC系列:什么是gRPC API,它如何工作?

清单5:对演示XML-RPC API服务器上的ping方法的响应

标准XML声明在上面清单5的第1行运行。第2行后面是必需的XML-RPC元素 ,该元素表示XML是对对API的请求的响应。第3行具有 标记,其中将包含各种响应元素。在每个元素中,将有一个 标记。值标签将包含另一个根据值的标量类型命名的标签。在这种情况下,清单5的第6行只有一个返回的参数。它是标量类型 ,其值为[“ I like ice cream”]。

注意,在第6行,将string-returned参数的值格式化为JavaScript数组。将字符串格式化为数组是完全任意的。返回表示数组的字符串参数是由API库的设计人员决定的,您已选择该API来帮助建立XML-RPC API。另一个库尽可能将字符串作为对象或纯字符串返回。因此,那些使用特定XML-RPC API的用户会很好地意识到从XML-RPC API调用返回的字符串格式。

选择XML-RPC解析库是一个重大决定

作为本系列研究的一部分,我们研究了许多用于实现XML-RPC客户端和服务器的库。我们发现,尽管所有库都根据XML-RPC规范支持XML的数据交换,但XML的呈现和报告将根据XML-RPC API实现中使用的库而有所不同。例如,某些库会将传入的XML转换为JSON或特定于语言的对象。这些库发布旨在一起使用的客户端和服务器。

因此,建议您在选择要与XML-RPC一起使用的库时,可能将自己锁定在以后可能很难更改的框架中。

处理数组

XML-RPC支持将数组作为XML元素 中然后是子元素 中子元素的链。

下面的清单6显示了一个XML示例,该示例描述了对在Simple XML-RPC Server演示API上进行的名为add的XML-RPC过程的调用。XML将整数数组(在本例中为[0,2,2])传递给名为add的过程。该数组在第8 -19行中定义

gRPC系列:什么是gRPC API,它如何工作?

清单6:对XML-RPC过程add的调用,该过程采用整数数组求和

请注意,在<array> ... </ data> </ array>序列中有一个<value>元素集合。每个 元素都包含一个描述值的数据类型的元素。上面清单6中的第10行描述了数组中第一个元素的值,如下所示:<i4> 0 </ i4>。元素<i4>在XML-RPC规范中定义,用于描述整数。字符串数组会将每个值定义为<string>。

下面的清单7显示了来自名为add的XML-RPC过程的XML响应

gRPC系列:什么是gRPC API,它如何工作?

清单7:来自XML-RPC过程add的响应,该响应总结了一个整数数组

除了提交数组之外,XML-RPC还指定了一种将命名参数提交给过程的方法。这是使用<struct>元素完成的。

使用结构和命名参数

清单8显示了一个XML示例,该示例描述了对XML-RPC过程chatter的调用;简单XML-RPC演示API的方法之一。XML传递带有名称消息和限制的参数,作为XML-RPC定义的结构。名为message的参数的值为“ Hi there!”。名为limit的参数的整数值为3

gRPC系列:什么是gRPC API,它如何工作?

清单8:对自定义过程chatter的调用,该过程在包含两个命名参数的结构中使用命名值

下面的清单9显示了来自过程调用chatter的XML响应。注意,XML将调用结果显示为XML-RPC struct数组。每个结构都包含在请求中发送的消息以及一个值计数,该值报告结构在数组中的顺序。在数组中发出消息并报告值消息和计数是编程到聊天过程中的自定义逻辑。

gRPC系列:什么是gRPC API,它如何工作?

清单9:XML-RPC API过程,聊天程序以一个值数组作为响应,每个值包含一个结构

为什么我们在XML-RPC示例上花了那么多墨水?正如刚刚演示的那样,XML-RPC数组的格式可能会导致创建大量XML内容,从而使HTTP请求和响应变得非常大,从而导致网络上的传输和接收速度变慢。计划使用XML-RPC的开发人员需要意识到这一缺点。如上所述,可以使用.zip或.rar压缩来减少HTTP请求和响应的大小。同样,开发人员编写返回数组中数据的逻辑的开发人员可以使用分页将数组作为通过多个HTTP对话传递的一系列数据块返回数组。重要的是要意识到XML-RPC下的数组可能会变得很大,因此需要容纳较大的数组。

XML-RPC为处理远程过程调用带来了很大的灵活性,并允许开发人员以与语言无关的方式使用RPC。并且,该规范易于掌握。但是,XML-RPC确实有一个很大的缺点。该规范不允许定义自定义XML元素。例如,您不能在XML消息中添加元素<transactionId>作为在单个事务下组织大量HTTP请求/响应交互的方式。

事实证明,在1998年创建XML-RPC时,有关其后续产品的工作已经在进行中。这个更新的规范是简单对象访问协议(SOAP)。SOAP基本上旨在将XML-RPC提升到一个新的水平。

SOAP

简单对象访问协议(SOAP)与XML-RPC非常相似,它是一种用于与Internet上的过程或服务交换以XML编码的信息的协议。该规范于1999年公开,由W3C作为开放标准发布。

像XML-RPC一样,SOAP使用XML作为其消息格式,并支持HTTP作为传输机制。但是,SOAP以多种方式超越了XML-RPC的功能。首先,除HTTP之外,SOAP还可以由多种传输协议使用,例如SMTP和FTP。(典型的模式是使用HTTP进行同步数据交换,使用SMTP或FTP进行异步交互)。

另一个主要区别是SOAP使用标准XML模式(XSL)对XML进行编码。此外,开发人员可以创建自己的XML模式,以将自定义XML元素添加到SOAP消息中。最后,SOAP通常与Web服务描述语言(WSDL)一起使用。这意味着开发人员和机器可以检查支持SOAP的Web服务,以发现用于访问网络上的服务的细节,以及如何构造服务支持的SOAP请求和响应消息。通过WSDL进行的发现使使用SOAP消息对Web服务进行编程变得轻而易举。

SOAP消息的基本结构

在基本级别上,SOAP消息的结构是一个层次结构,其中根元素是<soap:Envelope>。该根元素可以包含三个子元素。(请参见下面的图5。)

gRPC系列:什么是gRPC API,它如何工作?

图5:SOAP消息的基本结构

如上图所示,元素<soap:Body>是必需的。元素<soap:Header>和<soap:Fault>是可选的。如果存在<soap:Header>元素,则它必须是<soap:Envelope>父级元素中的第一个子元素。如果存在元素<soap:Fault>,则它必须是元素<soap:Body>的子元素。

下表2说明了每个元素的用途。

组件 描述
Envelope 将XML文档描述为SOAP消息。
Header 包含头部信息
Body 与请求或响应有关的信息
Fault 包含有关消息处理期间发生的错误的信息。

如上所述,SOAP消息以XML编码。下面的清单10显示了SOAP消息以及嵌入在HTTP请求中的HTTP标头信息。第1至5行是HTTP请求中的标头条目。

gRPC系列:什么是gRPC API,它如何工作?

清单10:用SOAP编写的Web服务请求

第1行和第2行是通常的POST和Host标头。第3行声明请求的Content-Type包含根据SOAP规范进行编码的XML。第4行是请求正文的长度,其使用方式类似于XML-RPC中的用法。

第5行是必需的属性SOAPAction,它定义SOAP请求的意图。很多时候,支持SOAP的Web服务将根据SOAPAction标头中的信息将请求路由到服务的内部。

上面清单10中的第7至17行描述了SOAP消息。注意,在第8行和第9行声明了两个XML模式。在第8行,SOAP模式绑定到名称空间名称soap。在第9行定义了名称空间m。元素信封(第8行),标题(第10行) )和主体(第12行)是soap名称空间的一部分。元素GetStockPriceRequest(第13行)和StockName(第14行)是与XML名称空间m关联的自定义元素。

定制元素GetStockPriceRequest和StockName特定于提供目标SOAP API的Web服务。正如人们所期望的,Web服务使用这些自定义元素中的信息以一种特定于该服务的方式来处理SOAP消息的内容。对自定义名称空间的支持使SOAP以可控的方式扩展到无数用例。

在传输和消耗方面,清单10所示的消息可以是HTTP客户端和Web服务器之间的同步HTTP交换的一部分。但是,如上所述,SOAP与传输无关。因此,该消息可以像电子邮件一样容易地发送到在简单邮件传输协议(SMTP)下运行的电子邮件服务器。然后,电子邮件服务器中的智能将获取SOAP消息并进行处理。完成后,处理智能会在以后的某个时间点将响应作为对原始电子邮件的答复发送。通过电子邮件进行的对话实质上是一种异步交换。

下面的清单11显示了对上面清单10中的请求的SOAP响应。与区分<methodCall>和<methodResponse>消息的XML-RPC规范不同,SOAP规范在名称空间架构中没有提供这种区分。没有<soap:Request>或<soap:Response>。相反,区别在于在自定义名称空间中定义的XML元素。

下面的清单11显示了对清单10前面的GetStockPriceRequest的虚拟响应。请注意第13行的自定义元素<m:GetStockPriceResponse>。该元素的语义将消息的主体描述为响应。元素的名称为GetStockPriceResponse是任意的。可以很容易地将其命名为TickerResponse。要理解的重要一点是,请求/响应区别被委托给了自定义XML名称空间中定义的元素。

gRPC系列:什么是gRPC API,它如何工作?

清单11:用SOAP编写的Web服务响应

另外,请注意,上面清单11中第14-16行所示的元素是自定义名称空间m的一部分,并且包含与响应在语义上相关的信息。这是使用自定义名称空间扩展SOAP消息的含义的另一个示例。将标准SOAP模式与自定义模式相结合,使SOAP成为一种非常通用的数据交换格式。

利弊

使用SOAP有很多好处。该协议使用HTTP作为传输方式,但是,它也可以使用SMTP和FTP等协议。SOAP支持通过WSDL进行反射,并且它与语言无关,这在支持各种编程语言但需要使用通用语言进行消息交换的大型公司中具有显着优势。另外,在Web服务的早期,SOAP曾经非常流行。因此,仍然需要维护许多传统的SOAP实现,这使其成为一种非常有吸引力的赚钱方式。

如果COBOL教给我们任何东西,那就是旧代码不会消失。经过数十年的维护,它才逐渐消失。在可预见的将来,开发人员将需要维护和扩展现有的SOAP代码。随着SOAP开发人员的减少,其余精通SOAP的开发人员将能够获得高薪。供求规律有利于SOAP开发人员。

但是,与任何API架构模式一样,使用SOAP也有缺点。主要缺点是,因为SOAP基于XML,而XML又可能非常冗长(尤其是使用自定义名称空间),因此您必须在网络上移动大量文本才能完成工作。另外,SOAP规范需要花费一些时间来掌握。该协议具有较高的详细程度。例如,虽然规范支持使用SOAP消息进行远程过程调用的概念,实际上在SOAP下实现RPC要求将标准SOAP元素与自定义元素混合在一起。自定义元素需要提供SOAP规范要求的RPC语义,但是SOAP模式不提供。换句话说,您不能仅使用在标准SOAP模式中定义的XML元素来实现实际的RPC。您需要创建自定义元素来填补空白。

SOAP仍然有效

SOAP的优势之一是结合使用自定义名称空间扩展格式的功能和自定义命名空间附带的强类型化功能。这些都是非常诱人的好处,以至于新的SOAP API仍在发布中,特别是对于那些其中数据完整性至关重要的API。

最终的缺点是SOAP很旧。自1999年以来一直存在。早在Internet成立之初,SOAP就为基于Web服务的编程带来了巨大的力量。但是现在,数十亿人,更重要的是,物联网世界中的每一天每天都有数十亿(如果不是万亿)的机器在使用互联网。该协议的庞大性阻碍了达到以这种程度的网络规模运行所需的速度。

SOAP一次满足了真正的需求。但是那是那时和现在。如今,大量数据需要以闪电般的速度通过网络传输。人们希望现在而不是一个小时内观看电影。股票交易以纳秒而非毫秒为单位进行。除了SOAP和XML-RPC的功能之外,还需要更快,更有效的协议。满足当前需求的一种协议是gRPC。

gRPC的出现

如上所述,gRPC是由Google创建的,目的是满足公司对可在网络规模上经济高效地运行代码的速度的需求。确实,所有公司都希望代码能够快速执行,但Google的需求是快速重新定义这个词。Google运作的关键在于其索引整个Internet的能力。为了实现这样的壮举,需要三件事:第一闪电般的速度,巨大的规模以及非凡的机器效率。Google具有计算能力,并且具有实现其目标的联网功能。事情变得棘手的是围绕在应用程序之间传递数据的速度。

满足速度需求

不管是好是坏,在Internet上来回传递的大多数数据都是根据基于文本的格式构造的。HTML和XML一样都是基于文本的。这些是笨重的格式,因为它们需要打开和关闭标签。例如,仅通过标准的HTML以结构化的方式转换组成姓名John James的十个字符,就需要151个字符,如下面的清单12所示。

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
</head>
<body>
<div id="first_name">John</div>
<div id="last_name">James</div>
</body>
</html>

清单12:HTML是用于传输信息的庞大格式

用XML传输相同的信息需要94个字符,少于HTML中的字符数。不过,它很笨重。(请参见下面的清单13。)

<?xml version="1.0" encoding="ISO8859-1" ?>
<name>
  <first>Jone</first>
  <last>James</last>
<name>

清单13:XML提供了比HTML更简洁的数据结构方式

JSON是另一种流行的基于文本的数据格式,比XML更简洁。传输名称时,JSON中的John Doe仅需要47个字符,如下面的清单14所示。

{
  "first_name""John",
  "last_name""James"
}

清单14:JSON旨在成为一种简洁的基于文本的数据格式

尽管JSON是比HTML或XML更为简洁的格式,但就Google对速度和效率的需求而言,它仍然过于庞大。下表3说明了上述示例中以HTML,XML和JSON表示的John James名称的实际位数。(请记住,每个字符都是1个字节,等于8位。)

例子 字数 位数
清单4:HTML 151 1208
清单5:XML 96 768
清单6:JSON 47 376

表3:根据名字和姓氏,发送姓名John James所需的位数

当您考虑组成名称的10个字符的实际位数(仅包括80位空格的John James)时,当您需要按顺序排列时,上面表1中说明的所有格式都非常庞大谷歌的。特别是在网络上打包和传输数据时。需要更好的东西。更好的是协议缓冲区。

在gRPC中,所有数据均以二进制格式传输。信息被序列化为紧凑的位集合,然后通过网络发送。然后,当这些位到达目标目的地时,将它们反序列化为文本。gRPC中使用的二进制格式是协议缓冲区。使用协议缓冲区可以使数据快速传输,但确实要付出一定的代价,并且由于描述数据所带来的开销而产生了代价。

二进制数据格式的成本和收益

当您看一下上面清单中显示的HTML,XML和JSON时,您会注意到示例中的大多数文本都是用来描述数据的。再看一下JSON示例:

{
  "first_name""John",
  "last_name""James"
}

John James这个名字有十个字符,包括空格。但是,名称的JSON表示形式需要47个字符。这些多余的37个字符用于描述和构造数据。这是有必要的。没有该描述,我们将不知道数据是关于什么的。我们不知道JSON的内容是否是名称,更不用说James是名字还是姓氏了。因此,数据描述对于处理数据至关重要。

HTML,XML和JSON被称为自描述格式。该格式仅通过查看即可告诉您数据的含义。在上面的JSON示例中,我们知道John是名字,James是姓氏,因为JSON属性first_name和last_name直接描述字段。

自描述数据格式非常有用,但由于需要添加字符,因此在网络上传输也非常昂贵。但是,如果我们使用非自描述的数据格式怎么办?如果要传输的信息的源和目标都拥有一本“参考手册”,该手册使人们知道如何确定通过网络发送的比特集合中的字段的分段和顺序,该怎么办?从数据结构中删除自我描述会大大减少需要在网络上移动的数据的实际大小。

二进制数据格式支持的减小的大小是协议缓冲区的本质好处。但是需要权衡。使用自描述数据,您不需要源和目标都通用的“参考手册”。使用协议缓冲区,您可以做到。

就增加的复杂性和序列化/反序列化数据以及从二进制消息中解密有意义的信息所需的处理而言,使用协议缓冲区的成本更高。另一方面,这样做的好处是,一旦连接成功,数据移动速度就会更快。在设计gRPC时,Google选择接受费用以提高速度。当您将互联网作为一种生活方式编制索引时,十亿分之一秒的时间就算在内。因此,gRPC的基础是使用协议缓冲区二进制格式通过网络传输数据。

二进制序列化格式已经存在了一段时间

协议缓冲区是二进制编码的多种格式之一。还有一些Apache Thrift,Facebook在其某些后端服务中使用了Apache Thrift。Evernote还使用它在客户端设备和Evernote的云之间同步笔记本。另外,在分布式处理框架Hadoop中使用了Apache Avro。还有其他二进制格式。您可以在维基百科上阅读的各种数据序列化格式的完整列表,在这里。如今,对二进制数据格式所提供的那种提高速度的需求已经远远超出了Google。随着越来越多的人希望以越来越高的速度获取更多数据,越来越多的公司愿意接受与支持gRPC一起获得的技术开销。简而言之,gRPC正在成为主流。知道其工作原理以及最适合的应用程序不再是奢侈。必不可少

简而言之,gRPC框架

gRPC是一个API框架,支持通过HTTP / 2(本质上是万维网的版本2)上的点对点函数调用。要了解有关GRPC重要的是,它是一个规范,可以在任何能够支持在GRPC说明书中所述的要求的编程语言来实现。在撰写本文时,有多种语言的实现,包括但不限于GoLang,C#,C ++,Java,Objective-C,Ruby,Node.js和Python。

使用HTTP / 2可以使gRPC支持双向流传输(HTTP / 2的功能)。尽管gRPC允许客户端和服务器之间进行标准的请求/响应交互,但gRPC对HTTP / 2的依赖性意味着客户端可以以连续的比特流向服务器传输数据,反之亦然。此外,gRPC还可以使双向流同时发生(HTTP / 2的另一个功能)。结果是客户端可以将数据连续流传输到服务器,而同一台服务器同时将数据流传输回客户端。

HTTP / 2的另一个主要优点与系统资源的分配有关,尤其是在Linux上。正如您将在ProgrammableWeb的有关Ably.io的gRPC实现的案例研究中看到的那样,HTTP / 2提供了解决Linux内核所施加的网络连接限制的方法。在HTTP / 1.1中,每个请求和响应都需要一个唯一的连接。HTTP / 2支持在单个连接上进行多个请求/响应交换。

关于gRPC的另一个关键点是,如前所述,所有数据都使用协议缓冲区作为二进制数据在客户端和服务器之间传递。客户端和服务器都知道的.proto文件中描述了数据类型的定义(在gRPC中被称为消息)以及gRPC API发布的功能。您可以将.proto文件视为客户端和服务器都使用的上述“参考手册”。使用公共.proto文件类似于使用IDL(接口描述语言)在RPC交互中促进不同语言之间的进程间通信的模式。实际上,.proto格式是从IDL派生的。

下面的图6说明了gRPC背后的基本概念。请注意,客户端和服务器通过HTTP / 2进行通信,并且信息可以作为单个请求/响应事件或作为连续流进行交换。

gRPC系列:什么是gRPC API,它如何工作?

图6:描述gRPC API的模式在客户端和服务器共享的.proto文件中定义

另外,请注意,客户端和服务器都引用通用.proto文件中的架构,以确定要在客户端和服务器之间进行序列化和反序列化的消息的结构。(在本系列的后续文章中,我们将讨论用于允许gRPC服务器及其客户端共享.proto文件中定义的架构的不同技术。)

此外,.proto文件中的gRPC架构包含服务器发布的功能签名。根据发布的函数声明,客户端将使用此信息将消息传递给特定的函数。以下是将在.proto文件中定义的函数声明的示例。

rpc Add (Request) returns (Response) {}

在哪里:

rpc是保留协议缓冲区关键字,指示该函数是远程过程调用 Add是函数名称

(Request),表明该函数具有自定义消息类型的单个参数,请求 返回是保留协议缓冲区的关键字,指示在函数的返回类型 (Response)之前添加前缀表示该函数将返回类型为“ Response”的自定义消息

函数声明和函数实现之间有什么区别?

函数声明描述了函数名称,函数范围,输入参数和返回类型。根据语言的不同,将函数声明与函数实现分开。在Java中,函数声明是在Java接口中完成的,如下所示:

public interface Communicable {
        public String ping(String message);
     } 

然后,一旦在接口中声明了函数,就可以在Java类中实现它,如下所示:

public class Talker implements Communicable{
    @override
    public String ping(String message){
      System.out.print("I am going to return the message: " + message);
      return message;
    }
  } 

将函数声明与函数实现分开的模式在gRPC的概念级别上继续,其中函数声明在.proto文件 中完成,而实现则根据将.proto文件编程为实际行为的框架和语言完成。例如,在. proto文件中描述的函数add()如下所示:

rpc Add (Request) returns (Response) {}

可以使用Node.js gRPC库实现如下:

      function add(call, callback) {
const input = call.request.numbers;
const result = input.reduce((a, b) => a + b, 0);
callback(null, {result});
}

如您所见,使用.proto文件是使用gRPC API的重要方面。因此,让我们详细看一下。定义本文随附的简单服务演示项目的原型文件。对于打算使用gRPC API的任何人来说,了解协议缓冲区的语言规范的细节都很重要。

您可以在https://developers.google.com/protocol-buffers/docs/reference/proto3-spec中找到完整的协议缓冲区语言规范的当前版本。

定义.proto文件

下面的清单15显示了协议缓冲区.proto文件的内容,该文件是本文演示API Simple Service的基础。第1行是语法声明,用于声明所使用语言规范的版本。撰写本文时,协议缓冲区的当前版本为版本3。但是请注意,有许多有效的gRPC API正在使用版本2。

清单15,第3行是程序包声明,其中simplegrpc是程序包的任意名称。程序包类似于C ++,C#或Java中的名称空间。这是一种以通用名称组织.proto文件中的消息和功能的方法。

gRPC系列:什么是gRPC API,它如何工作?

清单15:描述简单的gRPC API的.proto文件

第5-43行描述了简单服务API支持的消息。关于消息声明格式的一件值得注意的事情是在消息中定义字段时使用字段索引号。让我们看一下使用索引号背后的原因。

了解协议缓冲区中的字段索引

看一下消息类型ChatterRequest的描述

message ChatterRequest {
    string data = 1;
    int32 limit = 2;
}

注意,ChatterRequest具有两个字段,data和limit。该数据字段的类型是串。字段限制的类型为int32。这些是协议缓冲区语言规范中定义的标准类型。另外,请注意,上面的字段数据分配了索引号1和限制分配给索引号2。使用索引号特别值得注意,因为与自描述格式(如JSON)不同,JSON的字段结构对数据结构是显式的,串行化协议缓冲区消息中的二进制数据缺少直接的字段命名。已知的是字段长度,因此是分配给该字段的值以及索引号。

只知道一个字段的值及其索引号是没有用的。您需要一种方法来确定该字段代表什么。这是.proto文件起作用的地方。想象这样的消息定义:

message Birthday {
    int32 day = 1;
    int32 month = 2;
    int32 year = 3;
}

每个字段包含一个数字。但是,我们是否要检查纯二进制格式的Birthday消息的实例,我们如何知道消息中的每个数字是什么意思?这个数字代表一个月吗?一年?一天?

为了解释字段值背后的含义,我们使用.proto文件作为“参考手册”。在上面的“生日”示例中,当需要将二进制消息反序列化为文本格式时,所有逻辑要做的就是根据.proto文件中消息定义中定义的已知索引号查找字段。然后,逻辑根据字段索引号提取字段名称。创建文本表示形式所需要做的就是将字段名称和字段值结合起来。下面的图7说明了使用索引号查找来确定字段名称的概念。

gRPC系列:什么是gRPC API,它如何工作?

图7:协议缓冲区下的二进制编码使用索引号来分段和标识字段

您可以在以下网址阅读协议缓冲区编码的详细说明:https://developers.google.com/protocol-buffers/docs/encoding

让我们继续介绍如何在.proto文件中定义服务。

了解服务和方法定义

上面清单5中的第45行是服务声明部分的开头。gRPC以服务名称组织所有功能。在这种情况下,服务名称为SimpleService。第46-57行定义了API发布的功能的签名。proto文件定义了六个函数,Add,Substract,Multiply,Divide,Ping和Chatter。这些功能签名的实现将在以后根据所使用的语言框架进行编程。简单服务演示API是使用Node.js平台实现的,因此功能逻辑将在服务器端JavaScript文件中进行编程。

函数Add,Subtract,Multiply和Divide的作用与它们的名称相同。每个函数都将Request消息作为参数,如上面清单6的第5行所定义。(为消息Request命名是任意的。)Request消息具有单个属性(数字),该属性是浮点值的数组。每个函数将根据其在数组中的顺序对数字数组执行其隐式操作。例如,函数Add([2,3,4,5])返回如下的Request消息:{“ result”:14},如下图8所示。

gRPC系列:什么是gRPC API,它如何工作?

图8:使用BloomRPC作为gRPC客户端应用程序,“简单服务添加”功能汇总提交的数组中的所有数字

函数Divide([2,3,4,5])返回以下请求消息:{“ result”:0.03333333333333333}。(请参见下面的图9。)

图9:Simple Service Divide函数根据每个数字在数组中的位置对提交的数组中的数字进行划分

Chatter(ChatterRequest)函数返回ChatterResponse消息流,如下图10所示。

图10:简单服务器功能,Chatter在流中返回ChatterResponse消息

流与数组

从上面的图9中可以看到,流传输是gRPC的强大功能,值得一提。

在此处阅读ProgrammableWeb的深入文章,该文章描述了如何在gRPC下构建流式API:(https://www.programmableweb.com/news/how-to-build-streaming-api-using-grpc/how-to/2020/02/21)

让我们再来看一下清单5中定义的(ChatterRequest)的函数签名,因为它是gRPC下流式传输的一个好例子。

rpc Chatter (ChatterRequest) returns (stream ChatterResponse) {}

请注意如何在returns子句(stream ChatterResponse)之后声明流。这意味着将建立一个HTTP / 2连接,并且在该连接期间将通过网络发送或“流式传输”一系列ChatterRequest消息。根据功能的实现方式,一旦流传输完成,该连接将被关闭。或者,它可以保持打开状态。这取决于程序员。

如果我想这样做,以便Chatter(ChatterRequest)将服务器端的所有ChatterResponse消息聚合到一个数组中,然后将所有数组作为函数返回的一部分发送出去,那么我将函数签名声明为:

rpc Chatter (ChatterRequest) returns (repeated ChatterResponse) {}

哪里

重复是一个关键字,它指示ChatterResponse消息的数组。

区分关键字流的使用和重复使用似乎是无关紧要的决定,但事实并非如此。想象一下,由于某种原因,Chatter函数的结果是5亿个ChatterResponse消息,超过1 GB的数据。当正在播放流时,由于每个ChatterResponse消息都来自HTTP / 2连接,因此远程客户端可以处理该函数的结果。但是,当使用关键字repeat时,从Chatter函数返回整个数组,客户端需要等待超过1 GB的数据下载才能进行处理。等待下载完成可能导致从进程阻塞到内存过载的各种问题。但是,这并不是说应该避免返回整个数据数组。在处理数据流或整个数组之间进行选择是适合于当前特定用例的决定。幸运的是,gRPC提供了支持这两种技术的灵活性。

放在一起

gRPC是API开发领域的重要框架。协议缓冲区的使用是gRPC规范的基础,它提供了一种促进通过网络快速传输数据的方法。另外,由于gRPC旨在在HTTP / 2上运行,因此gRPC API的开发人员和使用者可以享受HTTP / 2协议固有的流功能所带来的灵活性和强大功能。

使用gRPC有很多好处。但是,需要权衡取舍。使用gRPC需要大量的编程头脑。尽管其他API技术(例如REST)使精通HTTP原理的开发人员可以提高生产力,但gRPC要求的工作还很多。您需要了解如何使用协议缓冲区将信息序列化和反序列化为二进制数据,并且需要熟悉使用流。虽然确实有很多库可以为您完成很多工作,但事实仍然是,即使编写一个简单的客户端来使用来自gRPC API的数据,也需要很多专业知识。但是,如果您所在的企业中纳秒级的速度差异可能会有所不同,那么吸收重要的学习曲线就是值得付出的代价。

gRPC提供了很多功能,并且不会消失。每天都有越来越多的公司在使用它。最终,它将成为雇主中与其他已知技术(例如REST,AJAX和GraphQL)相当的一种期望技能。

接下来的是 本系列的下一部分将深入研究如何开发具有客户端和服务器组件的成熟gRPC应用程序