基本概念
- 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 调用
- 中心化的部署调度和流量控制
- 基于共享内存的通信协议
- 定制化的服务发现和连接池实现
- 定制化的服务启动和监听逻辑