这是我参与「第五届青训营 」伴学笔记创作活动的第 16 天
1.基本概念
本地函数和远程调用的区别
函数映射、数据转换成字节流、网络传输
IDL:通过一种中立的方式来描述接口,使得不同平台上运行的对象和用不同语言编写的程序可以相互通信。
生成代码:通过编译器工具把IDL文件转换成语言对应的静态库。
编解码:从内存中表示到字节序列的转换称为编码,反之为解码,也常叫序列化和反序列化。
通信协议:规范了数据在网络中的传输内容和格式。除必须的请求/响应数据外,通常还会包含额外的元数据。
网络传输:通常基于成熟的网络库走TCP/UDP传输。
RPC好处
- 单一职责,有利于分工协作和运维开发。
- 可扩展性强,资源使用率更优。
- 故障隔离,服务的整体性可靠性更高。
RPC问题
- 服务宕机,对方如何处理?
- 在调用过程中发生网络异常,如何保证消息的可达性?
- 请求量突增导致服务无法及时处理,有哪些应对措施?
将由RPC框架来解决!
2.分层设计
编解码层-数据格式
语言特定编码格式
这种编码形式好处是非常方便,可以用很少的额外代码实现内存对象的保存与恢复,这类编码通常与特定的编程语言深度绑定,其他语言很难读取这种数据。如果以这类编码存储或传输数据,那你就和这门语言绑死在一起了。安全和兼容性也是问题。如Java.io.Serializable。
文本格式
文本格式具有人类可读性,数字的编码多有歧义之处,比如XML和CSV不能区分数字和字符串,JSON虽然区分字符串和数字,但是不区分整数和浮点数,而且不能指定精度,处理大量数据时,这个问题更严重了;没有强制模型约束,实际操作中往往只能采用文档方式来进行约定,这可能会给调试带来一些不便。由于JSON在一些语言中的序列化和反序列化需要采用反射机制,所以在性能比较差。
二进制编码
实现可以有很多种,TLV编码和Varint编码。
编解码层-选型
- 兼容性
- 支持自动增加新的字段,而不影响老的服务,这将提高系统的灵活度。
- 通用性
- 支持跨平台、跨语言。
- 性能
- 从空间和时间两个维度来考虑,也就是编码后数据大小和编码耗费时长。
协议层-概念
协议是双方确定的交流语义,比如:我们设计一个字符串传输的协议,它允许客户端发送一个字符串,服务端接收到对应的字符串。这个协议很简单,首先发送一个4字节的消息总长度,然后再发送1字节的字符集charset长度,接下来就是消息的payload,字符集名称和字符串正文。
特殊结束符:过于简单,对于一个协议单元必须要全部读入才能够进行处理,除此之外必须要防止用户传输的数据不能同结束符相同,否则就会出现紊乱
HTTP协议头就是以回车(CR)加换行(LF)符号序列结尾。
变长协议:一般都是自定义协议,有header和payload组成,会以定长加不定长的部分组成,其中定长的部分需要描述不定长的内容长度,使用比较广泛。
网络通信层-Socket API
套接字编程中的客户端必须知道两个信息:服务器的IP地址,以及端口号。
socket函数创建一个套接字, bind将一个套接字绑定到一个地址上。listen监听进来的连接, backlog的含义有点复杂 这里先简单的描述:指定挂起的连接队列的长度,当客户端连接的时候,服务器可能正在处理其他逻辑而未调用accept接受连 接、此时会导致这个连接被挂起,内核维护挂起的连接队列,backlog则指定这个队列的长度,accept函数从队列中取出请求连接请求并接收它,然后这个连接就从挂起队列移除。如果队列未满,客户端调用connect马上成功,如果满了可能会阻塞等待队列未满(实际上在Linux中测试并不是这样的结果,这个后面再专门来研究)。Linux的backlog默认是128,通常情况下,我们也指定为128即可。
comnect客户端向服务器发起连接,accept接收一个连接请求,如果没有连接则会一直阻塞直到有新的连接进来。得到客户端的fd后就可以调用read,write函数和客户端通讯,读写方式和其他IO类似。
read 从fd读数据,socket默认是阻塞模式的,如果对方没有写数据,read会一直阻塞着:
write 写fd写数据,socket默认是阻塞模式的,如果对方没有写数据,write会一直阻塞着:
socket 关闭套接字,当另一端socket关闭后,这一端读写的情况:
尝试去读会得到一个EOF,并返回0。
尝试去写会触发一个SIGPIPE信号,并返回-1和errno=EPIPE,SIGPIPE的默认行为是终止程序,所以通常我们应该忽略这个信号,避免程序终止。
如果这一端不去读写,我们可能没有办法知道对端的socket关闭了。
网络通信层-网络库
- 提供已用API
- 封装底层Socket API
- 连接管理和事件分布
- 功能
- 协议支持:tcp、udp和uds等
- 优雅退出、异常处理等
- 性能
- 应用层buffer减少copy
- 高性能定时器、对象池等
3.关键指标
稳定性-保障策略
- 熔断:保护调用方,防止被调用的服务出现问题而影响到整个链路
- 限流:保护被调用方,防止大流量把服务压垮
- 超时控制:避免浪费资源在不可用节点上