[toc]
基本概念
本地函数调用
eg:
func main()
var a =2
var b =3
result calculate(a,b)
fmt.Println(result)
return
}
func calculate(x,y int)
Z :x*y
return Z
}执行步骤:
- 将值
a,b压栈 - 找到
calculate函数,进入函数栈中给x,y赋值 - 计算
x*y,结果保存在z中 - 从栈中取回
z返回值,赋值给result,打印
远程函数调用(RPC - Remote Procedure Call)
RPC和本地函数调用不同,RPC需要解决一下问题:
- 函数映射
- 数据流转换成字节流
- 网络传输
网络映射
本地函数调用直接找函数指针就行了,但是远程不行,地址空间都是不一样的,所以我们给每个函数给一个ID,在传输时附带id,执行时寻找。
数据流转换成字节流
本地:压栈,出栈
远程:客户端转换成字节流给服务端,服务端再转换成自己能读取的形式
网络传输
高效稳定地传输数据
RPC概念模型

Nelson提出rpc的过程由5个模型组成:User、User-Stub、RPC-Runtime、Server-Stub、Server
一次RPC的完整模型
IDL:Interface description Language,IDL使用中立的方式来描述接口,使得不同平台上的对象和不同语言编写的程序可以互相通信。
生成代码:指编译器工具将IDL转换成语言对应的静态库
编解码:也叫序列化和反序列化,从内存中表示到字节序列称为编码,反之为解码。
通信协议:规范了数据再网络中的传输内容和格式。除必须的请求/响应数据外,还包括额外的元数据
网络传输:基于成熟的网络库走 TCP/UDP 协议

RPC好处
- 单一职责,有利于分工协作和运维开发
- 可扩展性强,资源使用率更优
- 故障隔离,服务整体可靠性更高
RPC缺点
- 服务宕机如何处理
- 网络异常,如何保障消息的可达性
- 请求量突然增大如何及时处理?

小结
- 本地函数调用和RPC调用的区别:函数映射、数据流转换成比特流、网络传输
- RPC概念模型:User、User-Stub、RPC-Runtime、Server-Stub、Server
- RPC的完整过程
- 优缺点
分层设计
例子:Apache Thrift

编解码层
生成代码
IDL文件描述了接口的规范,这样即使不同语言编写的程序都可以使用。

数据格式
- 语言特定格式:编程语言将内存数据编码成字节序列,其他语言无法读取
- 文本格式:具有人类可读性的格式,性能比较差,如Json、XML、YAML
- 二进制编码:把数据转换成二进制流跨语言、高性能,常见的有
Thriift的BinarProtocol等
TLV编码:
- Tag:类型
- Length:长度
- Value:值,Value也可以是一个TLV
- tag和length占用了额外的空间

协议层
概念
特殊结束符:过于简单,对于一个协议单元必须要全部读入才能够进行处理,除此之外必须要防止用户传输的数据不能同结束符相同,否则就会出现紊乱。
HTTP 协议头就是以回车(CR)加换行(LF)符号序列结尾。

变长协议:一般都是自定义协议,有 header 和 payload 组成,会以定长加不定长的部分组成,其中定长的部分需要描述不定长的内容长度,使用比较广泛

协议构造

协议解析

网络通信层
Sockets API
位于传输层和应用层之间

网络库
- 对上层提供API
- 封装底层Socket API
- 连接管理和事件分发
- 协议支持
- TCP
- UDP
- UDS
- 优雅退出
- 异常处理
- 性能
- 应用层buffer减少copy
- 高性能定时器、对象池
小结
- RPC三个核心层
- 二进制编解码原理、选型
- 协议构造解析流程
- socket api调用流程
- 网络库指标
关键指标
稳定性
保障策略

熔断:保护调用方,防止调用服务出现问题而影响到整个链路
A调用B,B调用C。如果C超时了,那么B也会超时,那么A就会频繁的调用B,B会因为堆积大量请求导致服务宕机
限流:限制流量,防止大流量把线路压垮了
超时控制:被调用端响应过慢,调用端会主动停止不重要的请求,即使释放资源。
请求成功率
- 负载均衡:避免单个服务的负载过大
- 重试:调用失败会重试几次,超过次数才算真正的失败

长尾请求
长尾请求:响应时间明显高于平均响应时间的请求
pk99:按请求长度从小到大排列,超过99%的长度的会被认为长尾请求
处理长尾请求:在返回之前重新发送一次。按照过往经验来看99%的请求能在t3内返回,这时发送备份请求Req2,Req2会很快返回。

注册中间件(拦截器)
在创建时将这些功能可选地加上

易用性
- 开箱即用:合理的默认配置、丰富的文档
- 周边工具:生成代码、脚手架
扩展性
client发送消息会依次通过中间件处理,server亦然。

观测性
观测手段
- Log、Metric、Tracing
- 内置观测服务:Linux中的
top命令

性能
场景:
- 单机、多机
- 单连接、多连接
- 单/多 client/server
- 请求大小
- 请求类型:pingpong,streaming
目标:
- 高吞吐
- 低延迟
手段:
- 连接池
- 多路复用
- 高性能编解码协议
- 高性能网络库
企业实践——kitex
整体架构
kitex core:核心组件kitex byted:字节内部设施基础kitex tool:代码生成工具

自研网络库
原生网络库缺点:
- 无法感知连接状态:使用连接池时,池中存在失效连接,影响连接池复用
- 存在goroutine暴涨风险:一个连接一个goroutine,利用率低下
Netpoll
- 解决无法感知连接状态的问题:使用epoll主动监听
- 解决goroutine暴涨风险:建立goroutine池,复用goroutine
- 提升性能:引入
Nocopy Buffer,向上层提供Nocopy调用接口,减少拷贝
扩展性设计

性能优化
调度优化
- 优化
epoll_wait调度上的控制 - gopoll重用goroutine,降低同时运行的协程数量
LinkBuffer
- 读写并行无锁,支持nocopy流式读写
- 高效扩容缩容
- Nocopy Buffer池化,减少GC
Poll
引入对象池和内存池,减少GC开销
合并部署
参考
深入浅出 RPC 框架 副本.pptx - 飞书云文档 (feishu.cn)