前言:
gRPC-Web是一个JavaScript客户端库,可以使Web应用程序直接与后端gRPC服务进行通信,而无需HTTP服务器充当中介。
这意味着可以通过使用.proto文件定义客户端和服务器端数据类型和服务接口,轻松构建真正的端到端gRPC应用程序体系结构。因此,gRPC-Web为Web开发的整个REST范例提供了一种引人注目的新替代方案。
基础:
gRPC-Web能够使用.protodefinitions和自动生成客户端JavaScript在客户端Web应用程序和后端gRPC服务器之间定义服务“契约”(可以在Closure编译器JavaScript或更广泛使用的CommonJS之间进行选择)。
可以放弃开发过程:创建自定义JSON序列化和反序列化逻辑,处理HTTP状态代码(可能因REST API而异),内容类型协商等。从更广泛的架构角度来看,gRPC-Web使端到端gRPC成为可能。下图说明了这一点:
在左侧的gRPC-Web Universe中,客户端应用程序向GRPC后端服务器说出协议缓冲区,该服务器向其他gRPC后端服务说出协议缓冲区。
在右侧的REST Universe中,Web应用程序将HTTP发送到后端REST API服务器,然后该服务器将协议缓冲区称为后端服务。需要明确的是,REST应用程序本身没有任何问题。
使用REST API服务器构建了大量非常成功的应用程序,这些服务器使用非HTTP协议与后端服务进行通信。但是想象一下,这些应用程序的开发过程围绕着一个协议和一组.proto接口(以及一组服务契约),几乎可以感受到无数小时的节省和头痛的避免。
gRPC-Web的好处不仅仅是“技术”;他们也是组织的。明亮的橙色线不仅仅是一个不同的协议 - 它是一个独立的工作和认知负荷来源,现在可以很容易地变成亮绿色。
使用gRPC-Web的优点:
随着时间的推移,gRPC-Web将提供更广泛的功能集。但可以看到它从一开始就提供了一些重大胜利:端到端gRPC - 如上所述,使用gRPC-Web,可以正式从堆栈中删除REST组件并将其替换为纯gRPC,能够使用Protocol Buffers创建整个RPC管道。想象一下客户端请求转到HTTP服务器的情况,然后HTTP服务器与5个后端gRPC服务进行交互。将花费尽可能多的时间来构建HTTP交互层,因为将构建整个管道的其余部分。前端和后端团队之间更紧密的协调 - 回想上面的图表。使用Protocol Buffers定义整个RPC管道,不再需要将“微服务团队”与“客户团队”一起使用。客户端 - 后端交互只是一个gRPC层。老实说,还没有完全掌握端到端测试,服务网格集成,持续集成/交付等方面的影响。轻松生成客户端库 - 使用gRPC-Web,与“外部”世界交互的服务器,即连接后端堆栈和互联网的隔膜,现在是gRPC服务器而不是HTTP服务器,这意味着所有服务都是客户端库可以是gRPC库。需要Ruby,Python,Java和其他4种语言的客户端库吗?不再需要为所有这些客户端编写HTTP客户端。
一个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); }
Tip: 可以在GitHub看rawtodos.proto
可以使用该.proto定义使用protoc命令行工具生成CommonJS客户端代码:
protoc echo.proto --js_out=import_style=commonjs:./output --grpc-web_out=import_style=commonjs:./output
Tip: 可以在GitHub看rawprotoc-gen.sh
现在从后端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()}`); } } });
Tip: 可以在GitHub托管的rawtodo-service.js
同样,没有HTTP代码或方法,没有JSON解析,没有头协商。声明了数据类型和服务接口,并且gRPC-Web摘录了所有“硬接线”样板,提供了一个干净且人性化的API(基本上与当前用于gRPC API的Node.js相同的API,刚刚转移到客户端)。在后端,gRPC服务器可以用任何支持gRPC的语言编写,包括Go,Java,C ++,Ruby,Node.js等等(请参阅官方gRPC文档中的语言特定文档)。最后一块拼图是服务代理。从一开始,gRPC-Web将支持Envoy作为默认服务代理,它具有内置的envoy.grpc_web过滤器,只需几行复制和配置即可应用。
下一步
gRPC-Web正式发布版本意味着核心构建块已牢固到位,可以在生产Web应用程序中使用。但是gRPC-Web还有很多其他的东西要来。查看官方路线图,了解核心团队在不久的将来所设想的内容。gRPC-Web还存在着一些问题:
1、前端框架集成 - 常用的前端框架如React,Angular和Vue尚未提供对gRPC-Web的官方支持。希望看到这些框架能够支持它,因为每个框架都会从gRPC中受益匪浅。特定于语言的代理支持 - 从GA版本开始,Envoy是gRPC-Web的默认代理,通过特殊模块提供支持。但也很乐意看到特定语言的进程内代理的开发。进程内代理消除了对特殊代理(例如Envoy和nginx)的需求,并且使得使用gRPC-Web变得更加容易。
2、特定于语言的代理支持 - 从GA版本开始,Envoy是gRPC-Web的默认代理,通过特殊模块提供支持。也很乐意看到特定语言的进程内代理的开发。进程内代理消除了对特殊代理(例如Envoy和nginx)的需求,并且使得使用gRPC-Web变得更加容易。
结语
相信gRPC-Web的话题在社区随着时间推移,会越来越活跃。