RPC框架|青训营笔记

84 阅读5分钟

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

一、基本概念

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

  1. 函数映射
  2. 数据转换成字节流
  3. 网络传输

1.2 RPC概念模型

image-20230214172520682.png 五个模型组成:User、User-Stub、RPC-Runtime、Server-Stub、Server

主要是讲参数打包压缩后在网络传输,然后将结果返回

1.3 一次RPC的完整过程

image-20230214173133897.png

  • IDL文件(Interface description language):即描述接口文件,使得不同平台上运行的对象和用不同语言编写的程序可以相互通信。
  • 生成代码:通过编译器工具把IDL文件转换成语言对应的静态库
  • 编解码:解决跨语言数据交互的格式
  • 通信协议:规范传输内容和格式,以及额外的元数据
  • 网络传输:成熟网络库的TCP/UDP

1.4 好处 and 缺点

优点:

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

缺点:

  1. 基于网络传输,一旦网络异常,如何保证消息可达性?
  2. 服务器宕机,对方应如何处理?
  1. 请求量突增服务器无法及时处理

二、分层设计

image-20230214173605426.png

2.1 编解码层

  • 生成代码

    • 基于IDL生成不同语言对应的代码
  • 编码格式

    • 语言特定格式

    • 文本格式

    • 二进制编码

      • TLV 编码:Thrift 使用 TLV 编码(Tag+Length+Value)

      • Varint 编码:Protobuf 使用 Varint 编码

  • 如何选择编码格式:

    • 兼容性:支持自动增加新字段,不影响老的服务
    • 通用型:支持跨平台、跨语言,即使用人群多
    • 性能:空间和时间(编码解码所需时间,编码后数据的大小)

2.2 协议层

  • 消息切分

    • 特殊结束符
    • 变长协议:length+body,通常需要用定长的部分来描述长度
  • 协议构造

    • 以 Thrift 的 THeader协议为例讲解
    • image-20230214174438822.png
      • LENGTH: 数据包大小,不包含自身
      • HEADER MAGIC: 标识版本信息,协议解析时候快速校验
      • SEQUENCE NUMBER: 表示数据包的seqID可用于多路复用,单连接内递增
      • HEADER SIZE: 头部长度,从第14个字节开始计算一直到 PAYLOAD前
      • PROTOCOL ID: 编解码方式,有 Binary 和Compact 两种
      • TRANSFORMID: 压缩方式,如 zlib 和 snappy
      • INFO ID: 传递一些定制的 meta 信息
      • PAYLOAD: 消息体
  • 协议解析:

    • MagicNumber(读取协议类型)->PayloadCodec(读取编码方式)->Payload(解码)

2.3 网络通信层

  • 网络库

    • 提供易用的API

    • 功能:

      • 协议支持:TCP、UDP等等
      • 退出、异常处理等等
  • 核心指标

    • 吞吐高
    • 延迟低

三、关键指标

3.1 稳定性

  • 熔断:保护调用方,防止被调用的服务出现问题而影响到整个链路
  • 限流:保护被调用方,防止大流量把服务压垮(降级处理/返回限流异常)
  • 超时控制:避免浪费资源在不可用节点上(超时主动停掉不太重要的业务) (以上三种都是快速返回,避免资源浪费在不可调用的请求上,也是服务降级的手段)

3.2 请求成功率

  • 负载均衡

    • 服务节点负载尽量均匀,而不是某些节点负载过大
  • 重试(会加大直接下游的负载,有放大故障的风险) 防止重试风暴,限制单点重试和限制链路重试。

3.3 长尾请求

长尾请求一般是指明显高于均值的那部分占比较小的请求。业界关于延迟有一个常用的P99标准, P99 单个请求响应耗时从小到大排列,顺序处于99%位置的值即为P99值,那后面这1%就可以认为是长尾请求。在较复杂的系统中,长尾延时总是会存在。造成这个的原因非常多,常见的有网络抖动,GC,系统调度。

我们预先设定一个阈值 t3 (比超时时间小,通常建议是RPC请求延时的pct99),当Req1发出去后超过t3时间都没有返回,那我直接发起重试请求Req2,这样相当于同时有两个请求运行。然后等待请求返回,只要Resp1或者Resp2任意一个返回成功的结果, 就可以立即结束这次请求,这样整体的耗时就是t4,它表示从第一个请求发出到第一个成功结果返回之 间的时间,相比于等待超时后再发出请求,这种机制能大大减少整体延时。

image-20230214175856768.png

3.4 注册中间件

image-20230214180005565.png

3.5 易用性

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

Kitex使用Suite来打包自定义的功能,提供「一键配置基础依赖」的体验

  • 生成服务代码脚手架
  • 支持protobuf 和thrift
  • 内置功能丰富的选项
  • 支持自定义的生成代码插件

3.6 扩展性

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

执行流程图:(黄色部分即为中间件)

image-20230214180201366.png

3.7观测性

Log、Metric、Tracing

内置观测性服务

image-20230214180309812.png 除了传统的Log、Metric、Tracing 三件套之外,对于框架来说可能还不够,还有些框架自身状态需要暴露出来,例如当前的环境变量、配置、Client/Server初始化参数、缓存信息等。

3.8 高性能

这里分两个维度,高性能意味着高吞吐和低延迟,两者都很重要,甚至大部分场景下低延迟更重要。

四、课程总结

  • 从本地函数调用引出RPC的基本概念。
  • 重点讲解了RPC框架的核心的三层,编解码层、协议层和网络传输层。
  • 围绕RPC框架的核心指标,例如稳定性、可扩展性和高性能等,展开讲解相关的知识。
  • 通过对kitex讲解,讲述了当前互联网公司自研RPC框架的原因,拓展技术视野