这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记
通过这节课的学习,学习了解RPC的基本概念和相关知识,以下是课程学习笔记。
基本概念
本地函数调用
简单的例子(实际上编译器经常会做优化的)
远程函数调用(RPC-Remote Procedure Calls)
函数映射:
- 在本地,函数体直接通过函数指针来指定调用哪个方法,编译器就自动调用相应的函数指针
- 在远程调用中,函数指针是不行的,因为两个进程的地址完全不一样。每个函数都有自己的ID,在做RPC时要附上这个ID,还需要有ID和函数的对照关系表,通过ID找到函数并执行。 参数值传递:
- 在本地,只需要把参数压到栈里,然后函数自己去栈里读就行
- 在远程调用中,客户端服务端是不同的进程,不能通过内存来传递函数。需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式。 网络传输: 如何保持在网络上高效稳定的传输
一次RPC的完整过程
RPC的好处
1单一职责,有利于分工协作和运维开发
2.可扩展性强,资源使用率更优
3.故障隔离,服务的整体可靠性更高
RPC带来的问题
1服务宕机,对方应该如何处理?
2在调用过程中发生网络异常,如何保证消息的可达性?
3.请求量突增导致服务无法及时处理,有哪些应对措施?
使用RPC框架解决
RPC框架-分层设计
主要分为三层:编解码层、协议层、通信网络层
以Apache Thrift为例:
编解码层
生成代码
数据格式
语言特定的格式:许多编程语言都内建了将内存对象编码为字节序列的支持,例Java有java.io.Serializable
文本格式:JSON、XML、CSV等文本格式,具有人类可读性
二进制编码:具有跨语言和高性能等优点,常见的有Thrift的BinaryProtocol,Protobuf等
编码格式的选型
兼容性:支持自动增加新的字段,而不影响老的服务,这将提高系统的灵活度
通用性:支持跨平台、跨语言
性能:从空间和时间两个维度来考虑,也就是编码后数据大小和编码耗费时长
协议层
协议是双方确定的交流语义,比如:我们设计一个字符串传输的协议,它允许客户端发送一个字符串,服务端接收到对应的字符串。
特殊结束符:过于简单,对于一个协议单元必须要全部读入才能够进行处理,除此之外必须要防止用户传输的数据不能同结束符相同,否则就会出现紊乱
HTTP协议头就是以回车(CR)加换行(LF)符号序列结尾。
变长协议:一般都是自定义协议,有header和payload组成,会以定长加不定长的部分组成,其中定长的部分需要描述不定长的内容长度,使用比较广泛
协议构造:
协议解析:
网络通信层
Sockets API
应用层和传输层之间
知道两个信息:ip、端口
创建socket,bind操作,进行绑定,监听,accept(接收客户端发起的请求),read+writer通信,close。
网络库
提供易用API:封装底层Socket;API连接管理和事件分发
功能:协议支持: tcp、udp 和uds等;优雅退出、异常处理等
性能:应用层 buffer 减少copy;高性能定时器、对象池等
关键指标
稳定性-保障策略
稳定性-请求成功率
重试有放大故障的风险
首先,重试会加大直接下游的负载。如下图,假设A服务调用B服务,重试次数设置为r(包括首次请求),当B高负载时很可能调用不成功,这时A调用失败重试B,B服务的被调用量快速增大,最坏情况下可能放大到r倍,不仅不能请求成功,还可能导致B的负载继续升高,甚至直接打挂。
防止重试风暴,限制单点重试和限制链路重试
稳定性-长尾请求
长尾请求一般是指明显高于均值的那部分占比较小的请求。
业界关于延迟有一个常用的P99标准:单个请求响应耗时从小到大排序,顺序处于99%位置的值即为P99值,后面的1%就可以认为是长尾请求。
在较复杂的系统中,长尾延时总是会存在,造成的原因很多,例如:网络抖动、GC、系统调度
稳定性-注册中间件
易用性
开箱即用:合理的默认参数选项、丰富的文档
周边工具:生成代码工具、脚手架工具
扩展性
Middleware
Option
编解码层
协议层
网络传输层
代码生成工具插件扩展
观测性
Log、Metric、Tracing
内置观测性服务
高性能
场景:
单机多机
单连接多连接
单/多client|server
不同大小的请求包
不同请求类型:pingpong、streaming等
目标
高吞吐
低延迟
两者都很重要,甚至大部分场景下低延迟更重要
手段
连接池
多路复用:可以大大减少连接带来的资源消耗,并且提升了服务端性能
高性能编解码协议
高性能网络库