这是我参与「第五届青训营」伴学笔记创作活动的第 17 天
1. 概述
我将主要介绍如下知识点:
- RPC基本概念
- RPC框架分层设计
2. RPC基本概念
2.1 远程函数调用(RPC - Remote Procedure Calls)
RPC 需要解决的问题:
-
函数映射
-
在本地调用中,函数体是直接通过函数指针来指定的,我们调用哪个方法,编译器就自动帮我们调用它相应的函数指针
-
但是在
远程调用中,函数指针是不行的,因为两个进程的地址空间是完全不一样的。所以函数都有自己的一个ID,在做 RPC 的时候要附上这个 ID,还得有个 ID 和函数的对照关系表,通过 ID 找到对应的函数并执行
-
-
数据转换成字节流
-
在本地调用中,我们只需要把参数压到栈里,然后让函数自己去栈里读就行
-
但是在远程过程调用时,客户端跟服务端是不同的进程,不能通过内存来传递参数。这时候就
需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式
-
-
网络传输
- 远程调用往往用在网络上,如何保证在网络上高效稳定地传输数据?
2.2 RPC 相关基本概念
-
IDL(Interface description language)文件:
- IDL 通过一种中立的方式来
描述接口,使得在不同平台上运行的对象和用不同语言编写的程序可以互相通信
- IDL 通过一种中立的方式来
-
生成代码:
- 通过编译器工具把 IDL 文件转换成语言对应的
静态库
- 通过编译器工具把 IDL 文件转换成语言对应的
-
编解码:
- 从内存中表示到字节序列的转换被称为编码,反之为解码,也称作
序列化与反序列化
- 从内存中表示到字节序列的转换被称为编码,反之为解码,也称作
-
通信协议:
- 规范了数据在网络中的
传输内容和格式,除了必须的请求/响应数据外,通常还会包含额外的元数据
- 规范了数据在网络中的
-
网络传输:
- 通常基于成熟的网络库走 TCP / UDP 传输
2.3 RPC 的好处
- 单一职责,有利于分工协作和运维开发
- 可扩展性强,资源使用率更优
- 故障远离,服务的整体可靠性更高
2.4 RPC 带来的问题
- 服务宕机,对方应该如何处理?
- 在调用过程中发生网络异常,如何保证消息的可达性?
- 请求量突增导致服务无法及时处理,有哪些应对措施?
解决方法:RPC 框架
3. RPC 框架的分层设计
3.1 编解码层
Client 和 Server 都依赖同一份 IDL 文件,生成不同语言的CodeGen
数据格式:
-
语言特定编码格式:
- 许多编程语言都内建了将内存对象编码为字节序列的支持,如 Java 有
java.io.Serializable - 优点:非常方便,可以用很少的额外代码实现内存对象的保存和恢复
- 缺点:这类编码通常与特定的编程语言深度绑定,其他语言很难读取这种数据。如果以这类编码存储或传输数据,那你就和这门语言绑死在一起了。安全和兼容性也是问题
- 许多编程语言都内建了将内存对象编码为字节序列的支持,如 Java 有
-
文本格式:
- JSON、XML、CSV 等文本格式具有人类可读性
- 但数字的编码多有歧义之处,比如 XML 和 CSV 不能区分数字和字符串,JSON 虽然区分字符串和数字,但是不区分整数和浮点数,而且不能指定精度,处理大量数据时,这个问题更严重了
- 没有强制模型约束,实际操作中往往只能采用文档方式来进行约定,这可能会给调试带来一些不便。由于 JSON 在一些语言中的序列化和反序列化需要采用反射机制,所以在性能比较差
-
二进制编码:
- 具备跨语言和高性能等优点,有很多种实现方式,常见有 Thrift 的 BinaryProtocol,Protobuf 等
选型:
- 兼容性:
- 支持自动增加新的字段,而不影响老的服务,这可以提高系统的灵活度
- 通用性:
- 支持跨平台、跨语言
- 性能:
- 从空间和时间两个维度来考虑,也就是编码后数据大小和编码耗费时长
3.2 协议层
概念:
-
特殊结束符:
-
以一个
特殊字符作为每个协议单元结束的标志
-
-
变长协议:
-
以
定长加不定长的部分组成,其中定长的部分需要描述不定长的内容长度,使用比较广泛
-
协议构造:
-
LENGTH:
- 32bits,
数据包的字节大小,不包含自身长度
- 32bits,
-
HEADER MAGIC:
- 16bits,值为:0x1000,用于
标识协议版本信息,协议解析的时候可以快速校验
- 16bits,值为:0x1000,用于
-
FLAGS:
- 16bits,为
预留字段,默认值为 0x0000
- 16bits,为
-
SEQUENCE NUMBER:
- 32bits,表示
数据包的seqID,可用于多路复用,最好确保单个连接内递增
- 32bits,表示
-
HEADER SIZE :
- 16bits,等于
头部长度字节数 / 4,头部长度计算从第14个字节开始,一直到PAYLOAD前(注:header的最大长度为64K)
- 16bits,等于
-
PROTOCOL ID:
- uin8 编码,
编解码方式,有 Binary 和 Compact 两种
- uin8 编码,
-
TRANSFORM ID:
- uint8编码,
压缩方式,有 zlib 和snappy 等方式
- uint8编码,
-
INFO ID:
- uint8编码,用于
传递一些定制的meta信息
- uint8编码,用于
-
PAYLOAD:
- 消息内容,
消息体
- 消息内容,
3.3 网络通信层
网络库:
- 提供易用 API
- 封装底层 Socket API
- 连接管理和事件分发
- 功能
- 协议支持:tcp、udp 和 uds 等
- 优雅退出、异常处理等
- 性能
- 应用层 buffer 减少 copy
- 高性能定时器、对象池等
4. 总结
RPC 带来好处的同时也带来了不少新的问题,由 RPC 框架来解决;RPC 框架主要核心有三层:编解码层、协议层和网络通信层
参考:
- 字节内部课:深入浅出 RPC 框架