grpc
ddatsh
RPC :数据编码(内存对象
与可传输的字节流
转化),请求映射
数据编码
XML 日薄西山,JSON 风头正盛,Protobuf 方兴未艾
why Protobuf ?谷歌出品,某些场景下效率比 JSON 高
所有的优化都是有代价的。思考选择什么和放弃什么
JSON 缺点 & Protobuf 选择
{ "int":12345, "str": "hello", "bool": true }
{ "int":67890, "str": "hello", "bool": false }
-
非字符串的编码低效
int value 12345,内存表示只占两个字节,转成 JSON 却要五个字节。 bool 字段则占了四或五个字节。
-
信息冗余
同一个接口同一个对像,只是 int 字段的值不同,每次都还要传输"int"这个字段名
JSON 在可读性和编码效率之间选择了可读性,所以效率方面做了一定的牺牲
Protobuf 一方面用 VarInts 对数字进行编码,解决效率问题;另一方面给每个字段指定一个整数编号,只传字段编号,解决冗余问题
接收方如何知道各个编号对应哪个字段?只能事先约定。Protobuf 用 .proto 文件记录字段和编号的对应关系
message Demo {
int32 i = 1;
string s = 2;
bool b = 3;
}
Protobuf 提供了一系列工具,为 proto 描述的 message 生成各种语言的代码
传输效率上去了,工具链也更加复杂了。gRPC 抓过时一定会怀念 JSON 的
请求映射
.proto 作为 IDL,Protobuf 可做很多 JSON 不方便做的事情。其中最重的就是 RPC 描述!
package demo.hello;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
gRPC 就是用了 Protobuf 的 service 来描述 RPC 接口
proto 文件定义了 Greeter 服务, SayHello 方法,接受 HelloRequest 消息并返回 HelloReply 消息。如何实现这个 Greeter 则是语言无关的,所以叫 IDL
/{包名}.{服务名}/{接口名}
Greeter 服务对应的路径 /demo.hello.Greeter/SayHello
gRPC 协议规定Content-Type
header 取值 application/grpc
,也可application/grpc+proto
想用 JSON 编码,也可application/grpc+json
gRPC 通信(非流式调用,unary)内容
请求内容
POST /demo.hello.Greeter/SayHello HTTP/1.1
Host: grpc.demo.com
Content-Type: application/grpc
Content-Length: 1234
<Length-Prefixed Message>
响应内容
HTTP/1.1 200 OK
Content-Length: 5678
Content-Type: application/grpc
<Length-Prefixed Message>
stream rpc
gRPC 持三种流式接口,参数前加上 stream 关键字
- 请求流(推送或者短信)
- 响应流(订阅消息通知)
- 双向流(实时语音转字幕)
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc SayHello (stream HelloRequest) returns (HelloReply) {}
rpc SayHello (HelloRequest) returns (stream HelloReply) {}
rpc SayHello (stream HelloRequest) returns (stream HelloReply) {}
}
引入Length-Prefixed Message,同一个 gRPC 请求的不同消息共用 HTTP 头信息,给每个消息单独加五字节前缀来表示压缩和长度信息
HTTP/1.1 也支持复用 TCP 连接,但所有请求必须排队(请求、等待、响应)顺序进行。先到先服务。而在实际的业务场景中肯定会有一些请求响应时间很长,客户端在收到响应之前会一直霸占着TCP连接。在这段时间里别的请求要么等待,要么发起新的 TCP 连接。一言以蔽之,HTTP/1.1 不能充分地复用 TCP 连接
HTTP/2 引入 stream概念 ,解决 TCP 连接复用的问题(同样有取舍问题)。可简单理解为逻辑上的 TCP 连接,在一条 TCP 连接上并行收发 HTTP 消息,而无需像 HTTP/1.1 那样等待