RPC 原理与实现 | 青训营笔记

96 阅读3分钟

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

基本概念

本地函数调用

func main() {
    var a int = 2
    var b int = 3
    result := calculate(a, b)
    fmt.Println(result)
}

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

远程函数调用

实现RPC调用需要具备的三个功能

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

PRC概念模型

PRC 的过程由 5 个模型组成:User、User-Stub、RPC-Runtime、Server-Stub、Server

RPC过程

  • IDL -> 生成代码 -> 编解码 -> 通信协议 -> 网络传输 IDL:接口定义文件,如 protobuf 和 thrift 生成代码:protobuf 和 thrift 有对应的编译器,可以自动生成代码编解码:可读性强的有 json、xml 等,还有语言专属的编解码,例如 go 的 gob,二进制编解码有 protobuf 和 thrift

RPC的优点

  • 单一职责,一个模块对应一个服务
  • 可扩展性强,资源利用率高
  • 故障隔离,可靠性强

使用RPC要注意的问题

  • 服务宕机
  • 如何保证消息可达
  • 请求量徒增

RPC分层设计

编解码层

数据格式

语言特定的格式

  • 许多编程语言都内建了将内存对象编码为字节序列的支持,例如 Java 有 java.io.Serializable

文本格式

  • JSON、XML、CSV 等文本格式,具有可读性

二进制编码

  • 具备跨语言和高性能等优点,常见有 Thrift 的 BinaryProtocal, Protobuf 等

    • TLV 编码:Thrift 使用 TLV 编码

    • Varint 编码:Protobuf 使用 Varint 编码

选型

兼容性

  • 支持自动增加新的字段,而不影响老的服务,这将提高系统的灵活性

通用性

  • 支持跨平台、跨语言

性能

  • 从空间和时间两个维度来考虑,也就是编码后数据大小和编码耗费时长

协议层

  • 特殊符号结尾
  • 不定长,例如 length、value、length、value 格式

网络通信层

  • 在实际中,通常使用封装好的网络库来作为 RPC 框架的网络通信层

RPC关键指标

  • 稳定性:熔断,限流,超时控制,负载均衡,重试(例如错误重试,或者长尾请求设置超时时间重试),这些策略通过中间件注册到 RPC 服务中。
  • 易用性:开箱即用,周边工具(代码生成、脚手架工具)
  • 扩展性:middleware,option,编解码层,协议层,网络传输,代码生成扩展工具都不是写死的。
  • 观测性:log,metric,tracing(链式跟踪)、内置观测服务(即时报警)
  • 高性能:目标(高吞吐,低延迟),手段(连接池,多路复用,高性能编解码协议 protobuf 或 thrift,高性能网络库(epoll))

高性能网络库

  • epoll 主动监听机制,感知连接状态
  • goroutine 池
  • 内存池、对象池,减少 GC 开销
  • 引入 NoCopy Buffer,实现编解码层面零拷贝

服务注册,服务发现

负载均衡策略:轮询,加权轮询,一致性哈希

服务治理