深入浅出RPC框架 | 青训营笔记

100 阅读5分钟

基本概念

  • 远程函数调用(RPC-Remote Procedure Calls)。
  • RPC需要解决的问题:
  1. 函数映射
  2. 数据转换成字节流
  3. 网络传输

RPC概念模型

  • 1984 Nelson:RPC由5个模型组成:User、User-Stub、RPC-Runtime、Server-Stub、Server

一次RPC的完整过程

  • IDL(Interface description language)文件:描述接口,使得不同程序和对象可以相互通信
  • 生成代码:把IDL文件转化为语言对应的静态库
  • 编解码(序列化和反序列化):从内存中到字节序列的转换称为编码,反之为解码
  • 通信协议:规范了数据在网络中的传输内容和格式
  • 网络传输:通常基于成熟的网络库走TCP/UDP传输

RPC的好处

  1. 单一职责,有利于分工协作和运维开发
  2. 可扩展性强,资源利用率更优
  3. 故障隔离,服务的整体可靠性更高

RPC的弊端

  • 服务宕机
  • 调用过程中服务异常,how to 保证消息可达性?
  • 请求量突增导致服务无法及时处理 👉通过构建RPC框架解决上述问题

分层设计

编解码层

生成代码

客户端和服务端会依赖同一份IDL文件,生成不同语言的CodeGen(服务)。

数据格式

  • 语言特定的格式:许多编程语言都内建了将内存对象编码为字节序列的支持,举个🍘,Java有java.io.Serializable。
  • 文本格式:JSON、XML等具有人类可读性的文本格式。
  • 二进制编码:具有跨语言和高性能等优点,常见的有Thrift的BinaryProtocol,Protobuf等。

二进制编码

TLV编码:

  • Tag:标签,对应类型(方便压缩)
  • Length:长度
  • Value:值,Value也可以是一个TLV结构

选择的权衡

兼容性(新旧服务兼容)、通用性(跨平台、跨语言)、性能(编码数据大小和耗费时长)

协议层

  • 特殊结束符:以一个特殊字符作为每个协议单元结束的标示
  • 变长协议:以定长加不定长的部分组成,其中定长的部分需要描述不定长的内容长度

协议构造

LENGTH、HEADER MAGIC、SEQUENCE NUMBER、HEADER SIZE、PROTOCOL ID、TRANSFORM ID、INFO ID、PAYLOAD(消息体)

协议解析

框架会先从内存里读取指定的一部分数据,根据协议的约定读取MagicNumber,从而获取协议类型,再继续读取PayloadCodec,从而获取编解码方式,再通过相应的编解码方式读取Payload,将协议解析,解析后交付上层处理。

网络通信层

Sockets API

  • 介于应用层和传输层之间。
  • 首先server端获取IP和端口,创建一个套接字,然后执行bind操作,把套接字绑定到对应的地址上,然后再执行listen,监听其他连接(挂到队列里)。如果client端有request请求就是accept接收,然后逐帧read、write,最后执行close操作。
  • 客户端只需要启动socket服务,建立连接connection,发送请求request,进行读写read and write,最后执行close操作。
  • 两者是同步进行的。

网络库核心指标

  • 提供易用API:封装底层Socket API,连接管理和事件分发
  • 功能:协议支持TCP、UDP、UDS等,优雅退出、异常处理等。
  • 性能:应用层buffer减少copy,高性能定时器、对象池等。

关键指标

稳定性

保障策略

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

请求成功率

  • 负载均衡:均匀调用节点,合理分配资源
  • 重试:服务重试

长尾请求

Backup Request(备份请求):设置超时时间,在Request发出后启动,如果时间结束还没返回响应,就可以重发请求。

注册中间件

通过注册中间件的方式把超时、熔断、重试、限流、负载均衡、Backup Request等的功能加上,灵活地注入各种服务治理策略,保障服务的稳定性。

易用性

  • 开箱即用:合理的默认参数选项,丰富的文档
  • 周边工具:生成代码工具、脚手架工具(生成服务代码脚手架等)

扩展性

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

观测性

  • 传统三件套Log(日志)、Metric(监视)、Tracing(跟踪)
  • 内置观测性服务(通过Http服务等了解内部信息框架配置、环境变量、线程、协程等)

高性能

  1. 目标:高吞吐、低延迟
  2. 场景:单/多机、单/多连接、单/多client/server、不同大小的请求包、不同请求类型(例如pingpong、streaming等)
  3. 手段:连接池(提高连接复用率)、多路复用(提高连接复用率)、高性能编解码协议、高性能网络库

企业实践

整体架构——Kitex

  • Kitex Core:核心组件(包括client、server等)
  • Kitex Byted:与公司内部基础设施集成(包括acl、config等)
  • Kitex Tool:代码生成工具(包括cmd、plugin等)

自研网络库

解决 原生库无法感知连接状态(引入epoll主动监听机制)、原生库存在goroutine暴涨的风险(建立goroutine池)、提升性能(引入Nocopy Buffer) 的问题。

扩展性设计

支持多协议,也支持灵活的自定义协议扩展

性能优化

网络库优化

调度优化、LinkBuffer(高效扩缩容)、Pool(引入对象池和内存池,减少GC开销)

编解码优化

  • Codegen:预计算并预分配内存,减少内存操作次数,包括内存分配和拷贝;Inline减少函数调用次数和避免不必要的反射操作等;自研了GO语言实现的Thrift IDL解析和代码生成器Thriftgo
  • JIT:改善用户体验(即时编译,减轻代码生成负担)、更强的编解码性能、高性能动态Thrift编解码器Frugal

合并部署

  • 将亲和性强的服务实例尽可能调度到同一个物理机,远程RPC调用优化为本地IPC调用
  • 框架改造:
  1. 中心化的部署调度和流量控制
  2. 基于共享内存的通信协议
  3. 定制化的服务发现和连接池实现
  4. 定制化的服务启动和监听逻辑