RPC 框架笔记 | 青训营

74 阅读7分钟

基本概念

  • RPC 即远程函数调用 (Remote Procedure Calls)
    • 单一职责,有利于分工协作和运维开发
    • 可扩展性强,资源使用率更优
    • 故障隔离,服务整体可靠性更高
  • RPC 需要解决
    • 函数映射:确定要调用的函数
    • 数据转换为字节流
    • 网络传输

RPC作为一种编程范式,通过抽象化网络通信细节,使开发人员能够像调用本地函数一样调用远程服务器上的函数,从而实现分布式应用程序之间的通信。无论是在微服务架构、移动应用开发还是跨服务器数据同步,RPC框架都在简化复杂性、提升效率以及增强可维护性方面发挥着至关重要的作用。

RPC 概念模型

RPC 的过程由 5 个模型组成

  • User
  • User-Stub
  • RPC-Runtime
  • Server-Stub
  • Server

用户(User)是整个通信过程的发起者和控制者。用户通过调用本地的用户存根(User-Stub)来发起远程过程调用请求。用户知道要调用的远程函数,并提供所需的参数。这个模型代表了应用程序中需要与远程系统通信的部分。

用户存根(User-Stub)是用户和RPC运行时(RPC-Runtime)之间的桥梁。它接受用户的调用请求,将调用的细节封装成消息,并将消息传递给RPC运行时。用户存根使得用户在调用远程函数时就像调用本地函数一样简单,隐藏了底层的通信细节。

RPC运行时(RPC-Runtime)是整个RPC过程的核心引擎。它接收用户存根传递的消息,负责将消息打包并通过网络发送给远程服务器。同时,它也负责接收远程服务器的响应消息,并将其解包,交给服务器存根处理。RPC运行时处理通信、序列化、反序列化等关键细节,确保数据正确传输并保持通信的可靠性。

服务器存根(Server-Stub)位于远程服务器端,充当了用户存根的对应部分。它接收RPC运行时传递的请求消息,解析请求的细节,然后调用服务器上的实际函数进行处理。服务器存根将处理结果打包成响应消息,并将其传递回RPC运行时。

服务器(Server)是远程系统的实际运行环境,承载了服务器存根执行的远程函数。服务器接收到服务器存根传递的请求消息后,执行相应的操作,生成处理结果,并将结果返回给服务器存根,进而返回给RPC运行时,最终传递给用户。

IDL

接口描述语言(Interface Description Language,简称IDL)是在分布式系统中定义和描述接口的标准化语言。它充当了各个系统、应用程序之间进行通信的桥梁,提供了一种统一的方式来定义数据结构、函数和方法,使得不同编程语言和平台之间可以无缝地进行远程过程调用(RPC)。

IDL具有抽象、中立、可移植的特性。通过使用IDL,开发人员可以定义接口的结构、函数签名、参数类型等信息,而不必关心实际的底层通信细节。这种抽象性使得不同的系统可以按照相同的规范定义接口,从而实现了跨语言、跨平台的互操作性。

IDL还可以通过生成工具将接口描述转化为各种编程语言的代码,从而在不同的系统中自动生成通信所需的代码。这消除了手动编写跨语言通信代码的繁琐性,大大提高了开发效率。

RPC 框架可处理的问题

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

RPC 框架分层设计

  • 用户自己编写的业务逻辑代码
  • (通过代码生成工具将 IDL 文件转换成不同语言对应的 lib 代码,封装编解码逻辑)
  • 框架的编解码层、协议层……
  • 框架的网络通信层

编解码层

  • 编码数据格式
    • 语言特定的格式:语言内建的将内存对象编码为字节序列的支持,例如Java的Serializable
    • 文本格式:JSON、XML、CSV 等文本格式,具有人类可读性
    • 二进制编码:具备跨语言和高性能等优点,常见有 Thrift 的 BinaryProtocol、Protobuf等
  • Thrift 的 BinaryProtocol 使用 TLV 编码
    • Tag:标签(类型)
    • Lenght:长度
    • Value:值(也可以是一个TLV结构)
  • 编码格式选型
    • 兼容性:支持自动增加新字段,而不影响老的服务,可提高系统的灵活度
    • 通用性:支持跨平台、跨语言
    • 性能:从空间和时间两个维度考虑(编码后大小和编码时长)

协议层

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

网络通信层

  • Socket API 介于应用层和传输层(TCP/UDP)之间
  • Server 端打开套接字,进行绑定和监听
    • 客户端发起请求时,accept,随后进入R/W
    • 读到EOF等信号后 close
  • 通常采用封装好的网络库作为网络通信层
    • 提供易用的API:封装底层 Socket API、管理连接和分发事件
    • 功能:协议支持 TCP/UDP/UDS 等,支持优雅退出、异常处理等
    • 性能:应用层 buffer 减少 copy,提供高性能定时器、对象池等

RPC 框架关键指标

稳定性

  • 保障策略
    • 熔断:保护调用方,防止被调用的服务出现问题而影响整个链路
    • 限流:保护被调用方,防止大流量把服务压垮
    • 超时控制:避免浪费资源在不可用节点上
  • 请求成功率
    • 负载均衡
    • 重试
  • 长尾请求
    • 明显高于平均响应时间的请求
    • Backup Request:不等待第一次请求返回,经过指定超时时间后就重发请求
      • 集群中有client、server1, server2三台机器,client需要向server请求数据,如果server1响应超时,则请求server2。server1、server2互为备份,包含同样的数据。client收到任意响应数据后立即通知其他请求过的server取消操作
      • 非幂等操作慎用
  • 注册中间件
    • 通过 Option 注入中间件

易用性

  • 开箱即用
    • 合理的默认参数、丰富的文档
  • 周边工具
    • 代码生成工具、脚手架工具

扩展性

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

观测性

在运行过程中需要观测服务状态

  • Log、Metric、Tracing(日志、指标、链路跟踪)
  • 内置观测性服务(状态暴露服务)

高性能

  • 场景:单机多机、单连接多连接、单/多Client Server、不同大小的请求包、不同请求类型
  • 目标:高吞吐、低延迟
  • 手段:连接池、多路复用、高性能编解码协议、高性能网络库

企业实践

  • 下一代微服务架构——服务网格 Service Mesh

合并部署

  • 微服务过微,传输和序列化开销越来越大
    • 将亲和性强的服务实例尽可能调度到同一个物理机,远程 RPC 调用优化为本地 IPC 调用
    • 中心化的部署调度和流量控制
    • 基于共享内存的通信协议
    • 定制化的服务发现和连接池实现
    • 定制化的服务启动和监听逻辑