网络协议打怪升级-应用层:RPC(gRPC)
1.RPC
1.1.RPC是什么
RPC全称(Remote Procedure Call Protocol),远程调用协议,主要用于异构的分布式系统间通信。
它更像是一种概念,或者说是一种技术模型,其核心思想为:
1
让你像调用本地接口一样调用远程服务器上的函数
1.2.RPC特性
- 目标
- 尽量保证提供类似本地调用的简洁语义的基础上,让分布式应用间的通信变得更加方便和高效
- 调用者无需显示区分本地和远程调用,同时基于RPC使得服务治理(
服务限流
、服务熔断
)更加方便
- 解决了哪些问题
- 不同服务可能由不同团队开发,跨语言接口需求不断增加
- 分布式服务之间的服务治理可以通过
RPC框架
解决
- 核心组件有哪些
- 客户端:发起rpc请求
- 客户端存根:存放服务端的服务列表,客户端直接调用存根,rpc将客户端的调用请求序列化打包并通过服务端发送到服务器
- 服务端口:接收rpc请求
- 服务端存根:接收客户端请求并解包,然后调用本地方法
- RPC的调用过程:
- 服务寻址:使用注册中心(Registry),小型服务可以直接配置静态IP
- 建立连接
- 网络传输
- 服务调用
- 响应返回
2.gRPC
2.1.gRPC概念
gRPC是对RPC理论的一种工业实现
和现代化升级
,由google在2015年开源,支持如下特性:
- 使用HTTP/2协议
- 支持多种HTTP/2特性,如多路复用,头部压缩,二进制传输等,传输效率高,网络包大小合适
- 序列化默认使用protobuf(protocol buffer)协议
- 多语言支持
- 支持多种调用模式
- 支持普通调用、流式调用(客户端/服务端/双工)
- 可通过插件实现多种扩展
- 负载均衡、拦截器、认证、支持转HTTP/HTTPS等
2.2.protobuf序列化
- protobuf 核心特性
特性 | 描述 |
---|---|
二进制格式 | 体积小,性能优 |
强类型 | .proto文件严格定义字段类型,代码生成 |
可选字段编号 | 每个字段都有可选唯一编号,方便升级兼容 |
版本兼容 | - |
多语言支持 | go,js,java,python… |
- protobuf 语法
1
2
3
4
5
6
syntax = "proto3"; //表示选择proto3语法
message Person { //结构体,struct
string name = 1; //字段编号
int32 age = 2;
}
- 使用protoc生成代码
1
protoc --go_out=. --go-grpc_out=. person.proto
生成的代码中包含:
- 消息结构体定义
- 序列化、反序列化方法
gRPC服务端&客户端接口
- 以客户端为例,看看protobuf的整个序列化、反序列化过程
1
2
3
4
5
6
7
8
9
10
11
客户端(结构体) ↓ Protobuf 序列化
[二进制请求数据]
↓ 通过 HTTP/2 发送
服务端收到
↓ Protobuf 反序列化
服务端内部调用函数
↓ 返回结构体
↓ Protobuf 序列化
[二进制响应数据]
↓ 客户端反序列化
→ 客户端得到响应结构体
.proto文件会被编译为二进制,字段编码为
连续的
“(字段编号+类型)+长度+值的形式” 以下message为例:1 2 3 4 5 6 7 8
message User { string name = 1; int32 age = 2; } name: "Joe" age: 30
- 字段类型-wire_type
字段类型 wire_type 用于的字段类型 Varint 0 int32,int64,bool 64-bit 1 double,fixed64 Length-delimited 2 string,bytes,message 32-bit 5 float,fixed32 由此我们可以通过二进制编码规则得到上述message的二进制编码
- 1-string name = 1;
1 2 3 4 5
字段类型:string -> Length-delimited -> 2 字段编号:1 (字段编号+字段类型):1 << 3 | 2 = 0x0A 字段长度:3 = 0x03 值Joe的utf-8字符串= 0x4A 6F 65
最终,编号为1的name字段的编码为: 0x0A,0x03,0x4A,0x6F,0x65
- 2-int32 age = 2
1 2 3 4
字段类型:int32 -> Varint -> 0 字段编号:2 (字段编号+字段类型):2 << 3 | 0 = 0x10 值30的16进制表示= 0x1E
最终,编号为2的age字段的编码为: 0x10,0x1E
得到该message的编码:0x0A,0x03,0x4A,0x6F,0x65,0x10,0x1E
为什么Varint字段类型不需要一个字节来表示字段值的长度?这就要提到Varint的编码机制-
可变长度编码机制
了 可变长度编码机制:对于Varint类型,使用每个字节的最高位表示值是否还有后续字节的表示。最高位表示是否还有后续字节,剩余7位表示具体值
也就是说,数据值越小时,占用的字节数就越少。数据值越大,占用字节数越多,且都用最高位表示是否还有后续。
3.总结
RPC(远程调用协议)是一种思想,旨在忽略语言与其他调用细节,使调用其他服务时如调用本地服务一样丝滑。
gRPC则是RPC的落地实现,它既是一个协议,也是一个框架,由Google在2015年开源。序列化默认使用protobuf协议,包传输采用http2协议。 具有序列化包小、传输性能强、支持多路复用等特性,在现代微服务架构中被广泛使用。
protobuf协议采用二进制编码的形式,将结构体编码为(字段编码+字段类型)+[字段长度(Length-delimited)]+字段值的形式,对于Varint这种类型,采用可变长度编码,减少了重复传输的数据量,大幅降低包大小,提高了传输效率。