😯 gRPC 与 REST - 简要比较
作者:黛博拉·埃梅尼
介绍
在当今时代,使用可扩展且高效的应用程序编程接口 (API) 构建应用程序极大地惠及了企业、开发者和用户。要构建交互式应用程序,您需要一个能够实现各种应用程序和服务之间通信和数据交换的 API。
因此,近年来开发出了许多有助于创建高性能 API 的框架。此类框架的一些例子包括表述性状态转移(REST)和谷歌远程过程调用(gRPC)。
本文将介绍 REST API 和 gRPC API,包括它们的定义、功能和区别。此外,您还将了解各种场景,这些场景突出了 REST 和 gRPC 的实际应用案例,以便您为项目选择最合适的框架。
我们将介绍以下步骤:
了解 API
为了构建功能强大的应用程序,开发人员通常需要集成来自外部的各种服务或与其他应用程序交换数据。API 在此过程中至关重要,它们充当不同系统之间的桥梁。API 定义了促进服务或应用程序之间通信的规则或协议。通过使用 API,开发人员可以访问其他应用程序的功能并将其集成到自己的应用程序中,从而专注于应用程序内的其他任务或功能。
让我们来看一个简单的例子,它描述了 API 及其在应用程序开发中的重要性。
<img src="https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-06-23-grpc-vs-rest/illustration.png" alt="grpc-vs-rest">
例如,假设您正在构建一个名为 BookFinder 的图书应用程序(提供有关图书的信息),并且想要为其设计一个 API。
为了增强应用程序的功能,您决定将其与图书数据库服务集成,并利用您在网上找到的 API。通过该 API,您可以访问诸如检索图书详细信息(例如标题、描述和作者)等功能。
具体流程如下:当用户在您的图书查找应用中搜索图书时,您会向图书数据库服务发出 API 请求,并将搜索查询作为参数传递。API 随后处理该请求并返回包含相关图书信息的响应。
REST API 说明
表述性状态转移(REST)API 是一种流行且被广泛接受的架构风格,它提供了一套指导 API 和微服务设计的原则。这种方法提倡以下几点:
-
可扩展性:REST API 采用无状态性等原则,这意味着客户端请求包含服务器处理该请求所需的所有信息。因此,服务器可以处理来自多个客户端的请求,确保系统能够应对不断增长的流量,从而实现可扩展性。
-
松耦合:REST 使用名为HTTP 的标准通信协议,并为客户端和服务端组件提供通用接口。由于组件遵循该通用接口,REST 促进了组件间的相互依赖性,这意味着对一个组件的更新或更改不会影响系统中的其他组件。这一过程被称为松耦合,它提高了模块化程度,并使系统中的组件更容易管理。
由于 REST API 构建于 HTTP(具体来说是HTTP/1.1版本)协议之上,因此它利用 GET、POST、PUT 和 DELETE 等 HTTP 方法对资源执行操作。在 REST API 的架构风格中,客户端使用这些 HTTP 方法之一向服务器发送请求以检索资源。服务器上的资源通过统一资源定位符(URL) 进行标识或访问。资源被识别后,服务器会以标准格式(通常是JSON,即 JavaScript 对象表示法)返回响应。
REST API 的工作原理
让我们来看一个例子,演示 REST API 的工作原理,使用上一节解释 API 的插图。
假设用户在您的图书查找应用上搜索名为“指环王”的书籍。以下步骤将执行:
步骤 1
您的应用程序(也称为客户端)将使用 HTTP 方法(GET)向包含书籍信息的数据库服务发送请求。该请求将发送到特定的 API 端点,例如 URL“ /api/books”,该端点已设计用于处理与书籍相关的请求。
查询字符串格式会将搜索查询作为参数包含在端点 URL 中。以下是请求示例:
GET /api/books?title=Lord%20of%20the%20Rings HTTP/1.1
Host: book-database-service.com
在请求中,搜索查询参数以键值对的形式指定。键代表参数名称,值代表搜索查询。在本例中,键为“title”,值为“Lord of the Rings”,该值已包含在请求 URL 中。为了处理空格和特殊字符,该值使用%20进行URL 编码。Host指示 API 所在的实际服务器。
步骤 2
收到请求后,数据库服务的 API 会对其进行处理,从 URL 中提取查询参数“ title ”,以确定用户正在搜索的书籍名称,例如“指环王”。
步骤 3
现在,API已经知道用户正在查找的书名,它会与数据库进行交互。API会利用搜索算法或使用类似SQL的数据库查询语言查询数据库,以在数据库集合中查找与指定书名匹配的书籍。
以下是检索到的书籍的详细信息:
{
"title": "Lord of the Rings",
"description": "The Lord of the Rings is the saga of a group of sometimes reluctant heroes who set forth to save their world from consummate evil..",
"author": "J. R. R. Tolkien”
}
第四步
由于数据库服务 API 采用 REST API 架构风格,它会将检索到的详细信息以JSON格式返回给您的 BookFinder 应用程序,如下所示:
HTTP/1.1 200 OK
Content-Type: application/json
{
"title": "Lord of the Rings",
"description": "The Lord of the Rings is the saga of a group of sometimes reluctant heroes who set forth to save their world from consummate evil..",
"author": "J. R. R. Tolkien
}
从 JSON 响应中,您可以观察到HTTP 响应 状态码,它指示请求的成功或失败。状态码200 OK表示请求成功,并返回书籍信息。
此外,请注意包含的Content-Type标头,它提供了有关响应的更多信息,表明响应正文包含 JSON 格式的数据。
第五步
在这里,您的 BookFinder 应用程序会接收来自数据库服务 API 的 HTTP 响应,并提取执行必要操作或将其显示给用户所需的信息。
gRPC API 说明
Google远程过程调用(gRPC)是一个现代框架,它为分布式系统中软件组件之间的通信提供了一个标准。它促进了请求和响应的交换。
gRPC 构建于RPC(远程过程协议)之上,RPC 是一种高速通信模型。它利用HTTP/2,允许客户端像在本地一样调用远程服务器上的函数。这种能力使开发人员能够集成用不同语言编写的服务。
由于 gRPC 构建于 HTTP/2 之上,因此它可以利用某些特性,从而带来以下好处:
-
高性能:gRPC 利用Protocol Buffers(一种用于序列化结构化数据的数据格式),从而高效地打包和发送数据。借助 HTTP/2 协议,gRPC 框架能够实现高速数据传输。这种高效快速的数据通信模式造就了高性能的应用。此外,gRPC 还确保了双向流式传输的高性能,支持客户端和服务器之间的同步数据传输。
-
兼容性和互操作性:用于定义 gRPC API 的语言称为Protobufs,它在定义服务和数据方面具有诸多优势。使用 Protobufs 提供了一种平台无关且语言无关的数据结构化方式。借助 Protobufs,您可以使用不同的编程语言编写代码,并且生成的数据可以与其他系统无缝通信。这种平台无关性提高了与其他系统的兼容性和互操作性。
gRPC API 的工作原理
让我们以您的 BookFinder 应用程序为例,来探索 gRPC API 的功能。
步骤 1
为了让您的应用程序用户能够搜索书籍并找到他们想要的那本,您首先需要创建一个名为“ book.proto ”的协议缓冲区文件。该文件将包含一个名为“book”的gRPC服务及其方法的定义BookService。您需要创建的方法之一是“book_details” BookSearch,它将接收一个搜索查询作为输入,并返回书籍的详细信息作为输出。
以下是book.proto文件的结构示例:
syntax = "proto3";
service BookService {
rpc BookSearch(BookRequest) returns (BookResponse) {}
}
message BookRequest {
string search_query = 1;
}
message BookResponse {
string title = 1;
string description = 2;
string author = 3;
}
上面的代码中使用proto3 ****版本的 protobuf 语法定义了 BookService 。它包含一个BookSearch方法,该方法接受一条BookRequest消息并返回一个对象BookResponse。BookRequest消息有一个名为 `<title>` 的字段search_query,由用户提供。BookResponse消息还包含书籍的 `<title>` title、description`<title>` 和 `<title> ` 字段author。
步骤 2
定义好 Protocol Buffer 文件后,您需要使用protoc 编译器和 Node.js 插件(如果您使用的是 Node.js)生成必要的 gRPC 代码。这将为您提供实现和使用 API 所需的服务器端和客户端代码。
接下来,您需要实现之前在book.proto文件中创建的 gRPC 服务方法。如果您使用的是Node.js,可以使用像@grpc/grpc-js这样的库(可通过 npm 获取)来创建服务器。您可以参考以下示例,使用协议缓冲区定义来实现服务器:
const grpc-js = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
// Loading the protocol buffer definition
const protoFile = 'book.proto';
const packageDefinition = protoLoader.loadSync(protoFile);
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
// Implementing the gRPC service methods
const BookService = {
BookSearch: (call, callback) => {
const searchQuery = call.request.search_query;
// Querying the book database for the book titled "Lord of the Rings"
const book = {
title: 'Lord of the Rings',
description: 'The Lord of the Rings is the saga of a group of sometimes reluctant heroes who set forth to save their world from consummate evil...',
author: 'J. R. R. Tolkien',
};
// Returning the book information as the response
callback(null, book);
},
};
// Creating a new gRPC server
const server = new grpc.Server();
// Adding the BookService implementation to the server
server.addService(protoDescriptor.BookService.service, BookService);
// Starting the server and binding it to a port
const port = 8080;
server.bind(`0.0.0.0:${port}`, grpc.ServerCredentials.createInsecure());
server.start();
console.log(`gRPC server started on port ${port}`);
上面的代码设置了一个 gRPC 服务器,监听端口8080上的传入请求。当客户端向该方法发送请求(搜索《指环王》一书)时BookSearch,服务器处理该请求并返回《指环王》一书的信息。
proto -loader模块用于加载和解析协议缓冲区定义。protoFile指定协议缓冲区定义文件(book.proto)的路径。通过使用protoLoader.loadSync(),协议缓冲区定义文件的内容将同步加载,并返回一个packageDefinition对象。
protoDescriptor利用grpc.loadPackageDefinition ()解析已加载的协议缓冲区定义,并生成用于 gRPC 通信的代码。
步骤 3
服务器启动后,您可以使用同一个协议缓冲区文件(book.proto)生成客户端代码。该客户端代码将提供一个客户端对象,允许您向服务器发出请求。
在客户端,您可以创建一个 gRPC 客户端(客户端代码),并使用它来向服务器的BookSearch方法发送请求。
以下是该实现方式的一个示例:
const grpc-js = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
// Loading the protocol buffer definition
const protoFile = 'book.proto';
const packageDefinition = protoLoader.loadSync(protoFile);
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
// Creating a new gRPC client
const client = new protoDescriptor.BookService(
'localhost:8080',
grpc.credentials.createInsecure()
);
// Creating a request object
const request = { search_query: 'Lord of the Rings' };
// Sending the request to the server
client.BookSearch(request, (error, response) => {
if (error) {
console.error('Error:', error.message);
return;
}
// Handle the response
console.log('Book Information:');
console.log('Title:', response.title);
console.log('Description:', response.description);
console.log('Author:', response.author);
});
上面的代码使用生成的客户端代码创建了一个新的 gRPC 客户端。客户端使用服务器地址 (localhost:8080) 和一个不安全的凭据进行初始化。然后,将搜索查询添加到请求对象中,并将字段设置为“指环王”。最后,gRPC 客户端调用该BookSearch方法,并将请求对象和一个用于处理响应的回调函数传递给它。
REST API 与 gRPC API 的特性有何不同?
由于 REST API 和 gRPC API 的特性不同,它们的架构风格也存在差异。下表列出了这些差异:
| 特征 | REST API | gRPC API |
|---|---|---|
| 消息格式 | 它主要使用 JSON、XML 和其他基于文本的格式。 | 它使用 Protocol Buffers 作为数据格式。 |
| 缓存功能 | 它支持使用 HTTP 标头(如 Content-Type、Authorization、Content-Length 等)进行缓存。 | gRPC API 没有内置的缓存机制,但可以使用 gRPC 客户端、NGINX、Redis、Memcached 等外部工具或库来实现缓存。 |
| HTTP 协议 | 它基于 HTTP/1.1 构建,利用 HTTP 方法(GET、POST、PUT、DELETE)。 | 它通过 HTTP/2 协议传输数据,在客户端和服务器之间建立双向通信。 |
| 代码生成 | 它方便使用 OpenAPI 和 Swagger Codegen 等代码生成工具和框架,使代码生成变得轻松高效。 | 它使用 Protobuf 进行代码生成,因为它允许不同编程语言之间进行高效且类型安全的通信,从而确保互操作性。 |
| 一体化 | 由于它基于广泛采用的标准协议 HTTP,因此得到了 Express.js、Laravel、Ruby on Rails、Django、ASP.NET 等各种框架的支持。 | 由于 Protocol Buffers (protobuf) 是一种与语言无关的消息格式,因此它支持各种编程语言,包括 Java、C++、Go、Python、Node.js、Ruby、PHP 等。 |
| 跨浏览器兼容性 | 由于它采用HTTP协议,因此任何支持HTTP的浏览器都可以访问它。例如,谷歌Chrome浏览器、Opera浏览器、Safari浏览器、Mozilla Firefox浏览器等。 | 由于 gRPC 主要设计用于服务器和客户端之间的通信,并且与基于浏览器的应用程序没有紧密耦合,因此它不直接依赖于浏览器。 |
结论
最后,您已经读完了这篇文章,了解了 REST API 和 gRPC API 的架构风格,包括它们的工作原理以及它们各自独特功能之间的区别。
资源
您可能会发现以下资源很有用:
文章来源:https://dev.to/refine/grpc-vs-rest-a-brief-comparison-4p8b