这是我参与「第五届青训营 」伴学笔记创作活动的第 16 天
概述
本节课程主要介绍 RPC 的分层设计的相关知识
分层设计
01. 分层设计 —— 以Apache Thrift为例
02. 编解码层
2.1 编解码层 —— 生成代码
2.2 编解码层 —— 数据格式
-
语言特定的格式
许多编程语言都内建了将内存对象编码为字节序列的支持,例如 Java 有 java.io.Serializable
-
文本格式
JSON、XML、CSV等文本格式,具有人类可读性
-
二进制编码
具备跨语言和高性能等优点,常见有 Thrift 的 BinaryProtocol,Protobuf等
2.3 编解码层 —— 二进制编码
TLV 编码
- Tag:标签,可以理解为类型
- Lenght:长度
- Value:值,Value也可以是个TLV结构
2.4 编解码层 —— 选型
-
兼容性
支持自动增加新的字段,而不影响老的服务,这将提高系统的灵活度
-
通用性
支持跨平台、跨语言
-
性能
从空间和时间两个维度来考虑,也就是编码后数据大小和编码耗费时长
03. 协议层
3.1 协议层 —— 概念
- 特殊结束符 一个特殊字符作为每个协议单元结束的标示
- 变长协议 以定长加不定长的部分组成,其中定长的部分需要描述不定长的内容长度
3.2 协议层 —— 协议构造
LENGTH:数据包大小,不包含自身 HEADER MAGlC:标识版本信息,协议解析时候快速校验 SEQUENCE NUMBER:表示数据包的 seqID,可用于多路复用,单连接内递增 HEADER SIZE:头部长度,从第 14 个字节开始计算一直到 PAYLOAD 前 PROTOCOL ID:编解码方式,有 Binary 和 Compact 两种 TRANSFORM ID:压缩方式,如 zlib 和 snappy INFO ID:传递一些定制的 meta 信息 PAYLOAD:消息体
3.3 协议层 —— 协议解析
04. 网络通信层
4.1 网络通信层 —— Sockets API
套接字编程中的客户端必须知道两个信息:服务器的 IP 地址,以及端口号。
socket 函数创建一个套接字,bind 将一个套接字绑定到一个地址上。listen 监听进来的连接, backlog 的含义有点复杂,这里先简单的描述:指定挂起的连接队列的长度,当客户端连接的时候,服务器可能正在处理其他逻辑而未调用 accept 接受连接,此时会导致这个连接被挂起,内核维护挂起的连接队列,baclog 则指定这个队列的长度,accept 函数从队列中取出连接请求并接收它,然后这个连接就从挂起队列移除。如果队列未满,客户端调用 connect 马上成功,如果满了可能会阻塞等待队列未满((实际上在 Linux 中测试并不是这样的结果,这个后面再专门来研究)。Linux 的 backlog 默认是 128,通常情况下,我们也指定为 128 即可。
connect 客户端向服务器发起连接,accept 接收一个连接请求,如果没有连接则会一直阻塞直到有连接进来。得到客户端的 ft 之后,就可以调用 read, write 函数和客户端通讯,读写方式和其他 I/O 类似
read 从 fd 读数据,socket 默认是阻塞模式的,如果对方没有写数据,read 会一直阻塞着;
write 写 fd 写数据,socket 默认是阻塞模式的,如果对方没有写数据, write 会一直阻塞着;
socket 关闭套接字,当另一端 socket 关闭后,这一端读写的情况:尝试去读会得到一个 EOF,并返回 0。
尝试去写会触发一个 SIGPIPE 信号,并返回 -1 和 errno = EPIPE, SIGPIPE 的默认行为是终止程序,所以通常我们应该忽略这个信号,避免程序终止如果这─端不去读写,我们可能没有办法知道对端的 socket 关闭了。
4.2 网络通信层 —— 网络库
- 提供易用API
- 封装底层 Socket API
- 连接管理和事件分发
- 功能
- 协议支持: tcp、udp 和 uds 等
- 优雅退出、异常处理等
- 性能
- 应用层 buffer 减少 copy
- 高性能定时器、对象池等
05. 总结
- RPC 框架主要核心有三层:编解码层、协议层和网络通信层
- 二进制编解码的实现原理和选型要点
- 协议的一般构造,以及框架协议解析的基本流程
- Socket API 的调用流程,以及选型网络库时要考察的核心指标
个人总结
我们都知道软件开发的过程很复杂,不仅是因为业务需求经常变化,更难的是在开发过程中要保证团队成员的目标统一。我们需要用一种可沟通的话语、可“触摸”的愿景达成目标,我认为这就是软件架构设计的意义。
但仅从功能角度设计出的软件架构并不够健壮,系统不仅要能正确地运行,还要以最低的成本进行可持续的维护,因此我们十分有必要关注系统的可扩展性。只有这样,才能满足业务变化的需求,让系统的生命力不断延伸。