RPC框架 | 青训营笔记

52 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天

基本概念

本地函数调用

func main() {
    result := calculate(2, 3)
    fmt.Println(result)
	return
}

func calculate(x, y int) {
    z := x * y
    return z
}

本地函数调用流程:

  1. 将参数2和3压栈
  2. 通过函数指针找到calculate函数,进入函数,取出栈中的2和3,将其赋予x和y
  3. 计算x*y,将结果存在z
  4. 将z的值压栈,然后从calculate返回
  5. 从栈中取出z返回值,赋值给result

远程函数调用(RPC - Remote Procedure Calls)

  • 函数映射
  • 数据转换成字节流
  • 网络传输

一次RPC的完整过程

![image-20230211010719519](/Users/rdstihz/Library/Application Support/typora-user-images/image-20230211010719519.png)

调用方

发起调用 -> 打包参数 -> 发送 -> 等待响应 -> 接收 -> 解包返回值 -> 返回

被调用方

接收 -> 解包参数 -> 调用函数 -> 获得返回值 -> 打包返回值 -> 发送

RPC的好处

  • 单一职责
  • 可扩展性强
  • 故障隔离

RPC带来的问题

  • 服务宕机,对方应该如何处理
  • 在调用过程中发生网络异常,如何保证消息的可达性
  • 请求量突增导致服务无法及时处理,有哪些应对处理

分层设计

RPC框架由编解码层、协议层、网络通信层组成。

编解码层

负责将参数、返回值等数据编码为字节码、将字节码解码为数据。

生成代码

Client和Server依赖同一个IDL文件,生成不同语言的CodeGen,生成代码包含数据类型(结构体等)。

数据格式

  • 语言特定的格式

依赖编程语言

  • 文本格式

如JSON、XML、CSV等文本格式。

  • 二进制编码

跨语言,高性能。常见的有Thrift的Thrift BinaryProtocol和Protobuf

TLV编码
  • Tag: 标签,可以理解为类型
  • Length: 长度
  • Value: 值,Value也可以是个TLV结构

编解码方式的选择

  • 兼容性
  • 通用性
  • 性能

协议层

概念

  • 特殊结束符

用一个特殊字符作为每个协议的单元结束标示

  • 变长协议

以室长加不定长的部分组成,定长部分需要描述不定长部分的长度

协议构造

将编码后的数据按一定的协议组织成数据包。

协议解析

从数据包中解析出数据。

网络通信层

网络层调用底层操作系统的Socket API发送和接收数据包。

Sockets API

![image-20230211011957796](/Users/rdstihz/Library/Application Support/typora-user-images/image-20230211011957796.png)

网络库

  • 提供易用API

封装底层Socket API

连接管理和事件分发

  • 功能

协议支持: TCP、UDP、UDS等

优雅退出,异常处理

  • 性能

应用层buffer减少copy

高性能定时器,对象池等

RPC框架的关键指标

稳定性

保障稳定性的策略

  • 熔断:保护调用方,防止被调用的服务出现问题而影响到整个链路
  • 限流:保护被调用方,防止大流量把服务压跨
  • 超时控制:避免浪费资源在不可用节点上

提高请求成功率

  • 负载均衡
  • 重试

处理长尾请求

指明显高于平均响应时间,占比较小的主动。

  • Backup Request: 发出请求后,如果预期时间内未收到响应,则再次发送主动。

实现策略的方式一般为注册中间件

易用性

开箱即用

周边工具

扩展性

  • Middleware
  • Option
  • 编解码层
  • 协议层
  • 网络传输层
  • 代码生成工具插件扩展

观测性

  • Log、Metric、Tracing
  • 内置观测性服务

高性能