RSS

gRPC-Web 已正式发布

我们很高兴宣布 gRPC-Web 的 GA 版本发布,这是一个 JavaScript 客户端库,它使 Web 应用能够直接与 gRPC 后端服务通信,而无需 HTTP 服务器充当中间层。“GA” 意味着 gRPC-Web 现已正式发布(Generally Available),稳定并合格用于生产环境。

借助 gRPC-Web,您现在可以通过使用 Protocol Buffers 定义客户端服务器端数据类型和服务接口,轻松构建真正的端到端 gRPC 应用架构。这在一段时间内一直是备受期待的功能,我们现在很高兴地宣布它已可用于生产环境。此外,能够访问 gRPC 服务为围绕 gRPC 的基于 Web 的工具开辟了新的令人兴奋的可能性。

基础知识

gRPC-Web 和 gRPC 一样,允许您使用 Protocol Buffers 定义客户端(Web)和后端 gRPC 服务之间的服务“契约”。然后可以自动生成客户端。为此,您可以在 Closure 编译器或更广泛使用的 CommonJS 之间进行选择。这种开发流程消除了管理诸如创建自定义 JSON 序列化和反序列化逻辑、处理 HTTP 状态码(这在不同的 REST API 中可能有所不同)、管理内容类型协商等问题的需要。

从更广泛的架构角度来看,gRPC-Web 实现了端到端 gRPC。下图对此进行了说明

图 1. 使用 gRPC-Web 的 gRPC (左) 和使用 REST 的 gRPC (右)

在左侧的 gRPC-Web 世界中,客户端应用程序使用 Protocol Buffers 与 gRPC 后端服务器通信,该服务器再使用 Protocol Buffers 与其他 gRPC 后端服务通信。在右侧的 REST 世界中,Web 应用使用 HTTP 与后端 REST API 服务器通信,该服务器再使用 Protocol Buffers 与后端服务通信。

使用 gRPC-Web 的优势

随着时间的推移,gRPC-Web 将提供更广泛的功能集,但以下是 1.0 版本中包含的内容

  • 端到端 gRPC — 使您能够使用 Protocol Buffers 构建整个 RPC 管线。想象一下这样一个场景:客户端请求发送到 HTTP 服务器,然后该服务器与 5 个后端 gRPC 服务交互。您很可能花费在构建 HTTP 交互层上的时间与构建整个管线的其余部分的时间一样多。
  • 前端和后端团队之间更紧密的协作 — 使用 Protocol Buffers 定义整个 RPC 管线后,您的“微服务团队”不再需要与您的“客户端团队”并存。客户端与后端之间的交互只是众多 gRPC 层中的又一层。
  • 轻松生成客户端库 — 借助 gRPC-Web,与“外部”世界交互的服务器,即连接您的后端堆栈到互联网的“膜”,现在是 gRPC 服务器而不是 HTTP 服务器,这意味着您服务的所有客户端库都可以是 gRPC 库。需要 Ruby、Python、Java 和其他 4 种语言的客户端库?您不再需要为它们全部编写 HTTP 客户端。

一个 gRPC-Web 示例

前面一节说明了 gRPC-Web 在大型应用中的一些高级优势。现在我们通过一个例子来进一步了解细节:一个简单的 TODO 应用。在 gRPC-Web 中,您可以从一个简单的 todos.proto 定义开始,如下所示

syntax = "proto3";

package todos;

message Todo {
  string content = 1;
  bool finished = 2;
}

message GetTodoRequest {
  int32 id = 1;
}

service TodoService {
  rpc GetTodoById (GetTodoRequest) returns (Todo);
}

可以使用以下命令从这个 .proto 定义生成 CommonJS 客户端代码

protoc echo.proto \
  --js_out=import_style=commonjs:./output \
  --grpc-web_out=import_style=commonjs:./output

现在,从后端 gRPC 服务获取 TODO 列表就和下面一样简单

const {GetTodoRequest} = require('./todos_pb.js');
const {TodoServiceClient} = require('./todos_grpc_web_pb.js');

const todoService = new proto.todos.TodoServiceClient('http://localhost:8080');
const todoId = 1234;

var getTodoRequest = new proto.todos.GetTodoRequest();
getTodoRequest.setId(todoId);

var metadata = {};
var getTodo = todoService.getTodoById(getTodoRequest, metadata, (err, response) => {
  if (err) {
    console.log(err);
  } else {
    const todo = response.todo();
    if (todo == null) {
      console.log(`A TODO with the ID ${todoId} wasn't found`);
    } else {
      console.log(`Fetched TODO with ID ${todoId}: ${todo.content()}`);
    }
  }
});

一旦您声明了数据类型和服务接口,gRPC-Web 就会抽象掉所有样板代码,为您留下一个干净且用户友好的 API(本质上与当前 Node.js 的 gRPC API 相同,只是转移到了客户端)。

在后端,gRPC 服务器可以用支持 gRPC 的任何语言编写,例如 Go、Java、C++、Ruby、Node.js 等等。最后一块拼图是服务代理。从一开始,gRPC-Web 就将支持 Envoy 作为默认服务代理,Envoy 内置了 envoy.grpc_web 过滤器,只需几行配置即可应用。

下一步

正式发布(GA)意味着核心构建模块已牢固就位,并可用于生产 Web 应用。但 gRPC-Web 未来还有更多内容。请查看官方路线图,了解核心团队对近期未来的愿景。

如果您有兴趣为 gRPC-Web 做出贡献,我们很乐意社区提供以下方面的帮助

  • 前端框架集成ReactVueAngular 等常用前端框架尚未提供对 gRPC-Web 的官方支持。但我们很乐意看到这些框架支持它,因为这些前端框架与 gRPC-Web 之间的集成可以成为为应用带来用户可感知性能优势的途径。如果您有兴趣为这些前端框架构建支持,请通过 gRPC.io 邮件列表在 GitHub 上提交功能请求 或通过下面的功能调查表告知我们。

  • 特定语言的代理支持 — 截至 GA 版本发布,Envoy 是 gRPC-Web 的默认代理,通过一个特殊模块提供支持。NGINX 也受支持。但我们也乐于看到为特定语言开发进程内代理,因为它们消除了对特殊代理(如 Envoy 和 Nginx)的需求,并将使使用 gRPC-Web 更加容易。

我们也非常欢迎社区提出功能请求。目前,提出功能请求的最佳方式是填写gRPC-Web 路线图功能调查表。填写表格时,请列出您希望看到的功能,并在我愿意为此贡献部分告知我们您是否愿意为这些功能的开发做出贡献。gRPC-Web 工程师在项目开发过程中一定会认真考虑这些信息。

最重要的是,我们要感谢过去一年中所有提供反馈、错误报告和拉取请求贡献的 Alpha 和 Beta 用户。我们衷心希望保持这一势头,并确保该项目为开发者社区带来切实的好处。