>[success] # gRPC 1. `rpc(Remote Produce Call)`是一个,在后台微服务开发阶段将每个模块进行独立,模块和模块之间要进行通信就需要去实现传输协议,`RPC`中可以使用`HTTP`作为通讯协议,通讯协议只是`RPC`中的一部分。**RPC中还有序列化、反序列化**等等。 2. [gRPC](http://www.grpc.io/)是一种基于 HTTP2 的现代[协议,它使用](https://hpbn.co/http2/)[协议缓冲区的强类型](https://developers.google.com/protocol-buffers/docs/overview)*二进制*数据格式跨多种语言,[gRPC-Web](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md)是一种尖端规范,支持从*现代*浏览器调用 gRPC 服务 ![](https://img.kancloud.cn/a2/da/a2da0ae6c25de6f2ff6dff39caae43cb_733x666.png) 3. **GRPC中序列化、反序列化**使用[Protocol Buffers](https://developers.google.cn/protocol-buffers/docs/proto3)是 google 开发的用于序列化数据的开源机制,通过创建一个带有`.proto`扩展名的文件来定义我们要发送/接收的数据的消息结构,以js转换为例来说`.proto`文件为使用的[`protobuf-javascript`](https://github.com/protocolbuffers/protobuf-javascript/releases)插件作为 转换为 `js` 文件 4. 通信`GRPC HTTP/2`实现都支持四种方法类型: 一元、服务器端、客户端和双向流(unary, server-side, client-side, and bi-directional streaming) * **Unary** 常见的客户端发起请求,服务端应答模式 * **Client-side Streaming** 用户以流的形式发起请求,源源不断,服务端再返回一次响应 * **Server-side Streaming** 用户发起一个请求,服务端以流的形式源源不断地返回响应 * **Bidirectional Streaming** 服务端、客户端以流的形式进行双向交互 [# gRPC的概念-- 四种流详细参考](https://juejin.cn/post/7048536076863930375#heading-9) >[success] # 浏览器使用gRPC * google-protobuf + grpc/grpc-web * google-protobuf + @improbable-eng/grpc-web >[info] ## grpc-web 方案 1. 使用 `grpc-web` 方案需要 **`proto`文件编译成 `js `代码,然后引入 `js `代码,调用提供好的 `grpc `方法** 2. 需要先下载[`protoc协议编译器安装`](https://github.com/protocolbuffers/protobuf/releases/) ,是为了可以转换`Protocol Buffers`,`Protocol Buffers`是谷歌的语言中立、平台中立、可扩展的结构化数据序列化机制一种协议,使用特殊生成的源代码轻松地使用多种语言在各种数据流中写入和读取结构化数据 3. 需要在下载`Protobuf` 转换成对应的语言的转换插件,以`grpc-web` 方案为例需要下载[`protoc-gen-grpc-web`](https://github.com/grpc/grpc-web/releases),如果你是`windows `电脑安装上面两个插件后,将`protoc-gen-grpc-web`(注意名字也要叫这个)放到`protoc`的`bin`目录 ~~~ . |-- bin | |-- protoc-gen-grpc-web.exe | `-- protoc.exe ~~~ 将配置增加到环境变量后,现在 可以执行(下面脚本意思将`greetHell.proto` 文件输出一份`js`版本,一份`web` 版本输出的格式为`js`)`js_out`此选项为请求和响应创建 JS 模型。(根据我们的原型文件,**输入** 和 **输出** 对象),`grpc-web_out`为浏览器创建 gRPC 客户端以发送 gRPC 请求并接收响应 ~~~ protoc -I=. ./greetHell.proto --js_out=import_style=commonjs:./ --plugin=protoc-gen-grpc=./protoc-gen-grpc-web.exe --grpc-web_out=import_style=commonjs,mode=grpcwebtext:./ ~~~ ![](https://img.kancloud.cn/73/ca/73ca1ceb6b36f17a91a887a855ab7df4_218x76.png) * 如果你执行过程中提示`# "--js\_out: options: mode is required" error on code generation` 可以将`protoc 降级到 v3.20.1`([link](https://github.com/protocolbuffers/protobuf/releases/tag/v3.20.1))**版本对应的是**[`Protocol Buffers`](https://developers.google.cn/protocol-buffers/docs/proto3)**语法版本** ***** 4. 如果就想使用最新[`Protocol Buffers`](https://developers.google.cn/protocol-buffers/docs/proto3)语法,在新的提议中 建议 js 转换使用单独的插件,你可以下载[`protobuf-javascript`](https://github.com/protocolbuffers/protobuf-javascript/releases)作为js 单独转换文件的工具,在配合[`protoc-gen-grpc-web`](https://github.com/grpc/grpc-web/releases)转换`web`版本代码(`protoc-gen-grpc-web`仅作web 准换不再新版本`protoc `插件中同时可以转换 `proto`文件为`js`) ***** * 你是`window `很遗憾你会遇到 `libstdsc++-6.dll was not found` `libwinpthread-1.dll was not found`两个报错 这是因为[`protobuf-javascript`](https://github.com/protocolbuffers/protobuf-javascript/releases)目前`Windows.exe` 编译程序还有问题,你的动手能力强可以[参考](https://github.com/protocolbuffers/protobuf-javascript/issues/142)解决方案,截至现在`3.21.2`还没有修复,因此你可能仍然只能使用**protoc 降级到 v3.20.1** 版本和**protoc-gen-grpc-web** 进行配合 ***** * 你可以使用Windows 的 `wsl` 安装一个`Linux`子应用,通过下载Linux 版本的[`protoc`](https://github.com/protocolbuffers/protobuf/releases/)、[`protoc-gen-grpc-web`](https://github.com/grpc/grpc-web/releases)、[`protobuf-javascript`](https://github.com/protocolbuffers/protobuf-javascript/releases)下载完成后,将文件中 `bin` 文件夹中文件移动到`user/bin` 中。例如`mv protoc /usr/bin/` 将三个插件 `bin` 执行文件都移动到`/usr/bin/` ~~~ protoc -I=. ./greet.proto --js_out=import_style=commonjs:. --grpc-web_out=import_style=commonjs,mode=grpcwebtext:. ~~~ 5. 使用 `ts` 版本安装,`protobuf-javascript` 只能转换文件为`js`,如果想让转换的文件为`ts` 需要使用插件[ `ts-proto`](https://github.com/stephenh/ts-proto) * 安装 `npm install ts-proto`,具体使用参看文档 ~~~ protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=. ./simple.proto ~~~ >[danger] ##### protoc 指令含义 这里以[`protoc-gen-grpc-web`](https://github.com/grpc/grpc-web) 为例 官方给的指令案例 ~~~ $ protoc -I=$DIR echo.proto \ --js_out=import_style=commonjs:$OUT_DIR \ --grpc-web_out=import_style=commonjs,mode=grpcwebtext:$OUT_DIR ~~~ * `-I`:input 作为指定的输入转译`proto`文件 * `js_out`/`grpc-web_out`会分别使用[`protoc-gen-grpc-web`](https://github.com/grpc/grpc-web/releases)和[`protobuf-javascript`](https://github.com/protocolbuffers/protobuf-javascript/releases)插件将 `proto`文件转换为`客户端`和`消息`这两个部分 ![](https://img.kancloud.cn/5e/ef/5eef20e6cdd1ea92491cb35df3566d7e_310x70.png) * `import_style`转换为 `js` 的代码格式,先看`protoc-gen-grpc-web`支持转换的类型有`closure`、`commonjs`、`commonjs+dts`、`typescript` 四种类型([Closure](https://developers.google.com/closure/library/)具体类型解释参考),`protobuf-javascript`转换格式只有`commonjs` * `mode`,作为`protoc-gen-grpc-web`类型请求有两种`mode=grpcwebtext`和`mode=grpcweb`,两者分别对应**application/grpc-web-text(Base64编码,文本格式) 和 application/grpc-web+proto(二进制格式),前者支持 Unary Calls 和 Server Streaming Calls,后者只支持 Unary Calls。** * `:` 表示输出生成文件地址 下面例子意思是将当前的文件下所有`.proto`转移后放到 `src/proto`文件下 ~~~ protoc *.proto --js_out=import_style=commonjs:../src/proto --grpc-web_out=import_style=commonjs,mode=grpcwebtext:../src/proto ~~~ >[danger] ##### 准备proto 文件 需要服务端提供的`proto` 文件当然也可以自己编写`proto` 文件 ~~~ syntax = "proto3"; // 指定使用proto3,如果不指定的话,编译器会使用proto2去编译 package greet; // 编译后的包名 message HelloRequest { // 定义请求对象类型 string name = 1; // string 类型name 字段1为 该成员编码时用1编号代替名字 } message HelloReply { // 定义响应对象类型 string message = 1; // string 类型name 字段1为 该成员编码时用1编号代替名字 } service Greeter { // 定义服务接口 //定义一个方法,SayHello 请求参数HelloRequest类型响应是HelloReply类型 rpc SayHello (HelloRequest) returns (HelloReply){}; } ~~~ * 对`package `字段做一个说明,当将`proto`文件进行转译后,导出的包是`module.exports = proto.greet;` 和`package ` 字段定义一致 ![](https://img.kancloud.cn/dc/3f/dc3f8a2c2e204dba7dbc3257a2257267_1035x329.png) >[danger] ##### 使用 生成的两个`*_web_pb.js` 和 `*_pb.js` 两个文件。分别引入了 ~~~ grpc.web = require('grpc-web'); // 和 var jspb = require('google-protobuf'); ~~~ 所以因此要引入对应两个包`google-protobuf` 只要`npm i google-protobuf` 但`grpc-web` 却给了两个选择`grpc/grpc-web` 和 `google-protobuf / @improbable-eng/grpc-web` >[info] ## grpc/grpc-web vs google-protobuf / @improbable-eng/grpc-web ![](https://img.kancloud.cn/7b/8b/7b8b193310fc73f9f1484ea217f76e41_736x191.png) 图片来源:[Envoy and gRPC-Web: a fresh new alternative to REST](https://blog.envoyproxy.io/envoy-and-grpc-web-a-fresh-new-alternative-to-rest-6504ce7eb880) `grpc-web` 并不是通过Http2 来实现的,这里要说明两点 * `grpc`协议层是通过`HTTP2`作为通信协议,但是gRPC 不能使用浏览器中`HTTP2`,因为[它们没有](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md)(并且可能不会)提供足够的 API ,因为**gRPC-Web**,无法强制直接使用浏览器的 `HTTP/2`底层的`API`,即使有浏览器也不会让`grpc-web`控制原始的 `HTTP/2`,因此`gRPC-Web` 只能从规范的角度出发,然后定义[差异](https://grpc.io/blog/state-of-grpc-web/),利用浏览器已有暴露的可控api去实现符合`grpc`定义规则。要明白浏览器是支持`HTTP2`发送请求,但已经高度封装,`gRPC-Web`需要的是更细致化的`HTTP2 API`的暴露去完成一些自己的自定义开发,但显然在浏览器这里行不通 * `grpc-web`其基本思想是让浏览器发送普通的 `HTTP `请求可以是`1.1` 也可以是`2.x`(但是发送形式使用 `Fetch `或 `XHR`,并不是我们所理解`GRPC`) ,并在 `gRPC `服务器前设置一个小代理,将请求和响应转换为浏览器可以使用的内容 ![](https://img.kancloud.cn/fe/3d/fe3d74e410efcc711022509655fd2985_477x480.png) ![](https://img.kancloud.cn/75/c5/75c5c3700807a3269b34cf5b0fe2dc5e_756x290.png) 谷歌有两个团队提供了`Grpc-web` 在浏览器的实现[官方 gRPC-Web](https://github.com/grpc/grpc-web)和[@improbable-eng/grpc-web](https://github.com/improbable-eng/grpc-web),之前说过 通信`GRPC`实现都支持四种方法类型: 一元、服务器端、客户端和双向流(`unary, server-side, client-side, and bi-directional streaming`),但在`gRPC-Web` 规范并没有明确要求任何客户端或双向流支持 * 关于[两个库的对比 The state of gRPC in the browser | gRPC](https://grpc.io/blog/state-of-grpc-web/#f5) ![](https://img.kancloud.cn/a1/80/a1805927b66db2e5b8e0724a1a13319d_894x323.png) * 但是`@immaybe-eng/grpc-web` 通过一个实验性的 websocket 传输支持客户端和双向流 * 二者客户端有不同的通信传输方式。 官方的 `gRPC-web` 只支持 `XMLHttpRequest`。 `@ immaybe-eng/grpc-web` 另外还支持 `Fetch` * 二者推荐使用的中间层也不同中间层(代理客户端和服务端发起,它处理来自客户端的 `XMLHttpRequest`或`Fetch`连接,并通过服务器的 `HTTP2 `将它们转换为 `gRPC`)`Envoy `作为[`官方gRPC-Web filter`](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/grpc_web_filter)代理,[grpcwebproxy](https://github.com/improbable-eng/grpc-web/tree/master/go/grpcwebproxy)作为 `@ immaybe-eng/grpc-web`代理方案 >[danger] ##### 关于 grpc-web 协议更多细节 * [对于 gRPC-Web 协议规范](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md) * 在未来`grpc-web` 也将会实现双向流(`bi-directional streaming`),但并不像`@immaybe-eng/grpc-web` 通过一个实验性的 `websocket `传输支持客户端和双向流而会使用[whatwg fetch/streams](https://github.com/whatwg/fetch)截止到`2022.11.7`现在为止现在此实现还没有成为正式提案 >[danger] ##### 优点 * **端到端 gRPC —** 如上所述,使用 gRPC-Web,您可以正式将 REST 组件从堆栈中删除,并将其替换为纯 gRPC,从而使您能够使用协议缓冲区来制作 *整个* RPC 管道。想象一个场景,客户端请求发送到 HTTP 服务器,然后与 5 个后端 gRPC 服务交互。您很有可能在构建 HTTP 交互层上花费的时间与构建整个管道其余部分的时间一样多。 * **前端和后端团队之间更紧密的协调**  ——回想上面的图表。通过使用 Protocol Buffers 定义整个 RPC 管道,您不再需要将“微服务团队”与“客户团队”放在一起。客户端-后端交互只是 gRPC 层中的一个。老实说,我还没有完全理解端到端测试、服务网格集成、持续集成/交付等的含义。 * **轻松生成客户端库** - 使用 gRPC-Web,与“外部”世界交互的服务器,即将后端堆栈连接到互联网的膜,现在是 gRPC 服务器而不是 HTTP 服务器,这意味着您的所有服务的客户端库可以是 gRPC 库。需要 Ruby、Python、Java 和其他 4 种语言的客户端库吗?您不再需要为所有这些编写 HTTP 客户端 >[danger] ##### 缺点 目前因为使用`base64` 进行传输实际体积也会变大,例如`1MB` 文件在传输过程中发现变成`1.4MB` >[info] ## 参考 [gRPC-web现状及测试](http://blog.itpub.net/31559359/viewspace-2637227/) [# HTTP/2 & gRPC — 高性能 RPC 框架](https://krishnakishorev.medium.com/http-2-grpc-high-performance-rpc-framework-a52722837920) [# gRPC-Web 通过 HTTP2](https://medium.com/@denis.zhbankov/grpc-web-via-http2-b05c8c8f9e6) [# 直接在你的 Go 服务器中代理 gRPC-Web](https://medium.com/safetycultureengineering/proxy-grpc-web-directly-in-your-go-server-without-envoy-7fbec326cb21) [# 使用 gRPC-Web 的优势](https://www.cncf.io/blog/2018/10/24/grpc-web-is-going-ga/) [推荐看一下 How to setup and test TLS in gRPC/gRPC-Web | by Alexey Vasyukov | ITNEXT](https://itnext.io/how-to-setup-and-test-tls-in-grpc-grpc-web-1b67cc4413e6)