Go后端:RPC框架·远程过程调用协议| 青训营笔记

182 阅读5分钟

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

简介

本文主要介绍了:

  1. RPC框架主要核心有三层:编解码层、协议层、网络通信层
  2. 二进制编解码的实现原理和选型要点
  3. 协议的一般构造,以及框架协议解析的基本流程
  4. 网络库的基本架构,以及选型时要考察的核心指标

1 基本概念

1.1 基本术语查询

RPC:(Remote Procedure Call Protocol)远程函数调用协议。通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。

IDL文件:(Interface Description Language)交互接口约定的描述文件。IDL通过一种中立的方式来描述接口,使得在不同的平台上运行的对象和用不同语言编写的程序可以相互通信。

生成代码:通过编译器工具把IDL文件转换为语言对应的静态库。

编解码:从内存中表示到字节序列的转换成为编码,反之为解码,也即“序列化与反序列化”。

通信协议:规范了数据在网络中的传输内容的格式。除必须的请求/响应数据外,通常还会包含额外的元数据。

网络传输:通常基于成熟的网络库走TCP/UDP传输。

长尾请求:响应时间明显高于平均响应时间的小部分请求。

1.2 概念引入与理解

本地函数调用远程函数调用

本地函数函数调用

main()函数调用本地函数calculate(x,y int)

  1. 将a和b的值压栈
  2. 通过函数指针找到calculate函数,进入函数取出栈中的值2和3,将其赋予x和y
  3. 计算x*y,并将结果存给z
  4. 将z的值压栈,然后从calculate返回
  5. 从栈中取出z返回值,并赋给result

远程函数调用

远程函数调用与本地函数调用的不同在于:远程函数的各种微服务之间为分布式、远程的部署和调用,服务之间相隔着网络。

那么,RPC需要解决的问题(以上图付款过程为例):

  1. 函数映射

    • 网上商城服务怎么告诉支付服务要调用付款函数而不是退款或者充值?
    • (答)对于本地函数调用可以通过函数指针找到对应函数,但是对于RPC来说,函数可能来自不同的进程,地址空间不同,不能直接调用。因此,需要给函数编好id,给出id与函数的对应关系,通过id找到所需函数。
  2. 数据转换成字节流

    • 参数怎么传递给远程服务?
    • (答)对于本地函数调用,传参是通过压栈出栈完成的,但对于RPC,其客户端与服务端是不同的进程,需要先从客户端将数据转换为字节流通过网络传给服务端,再将字节流解码成所需的格式。
  3. 网络传输

    • 怎么保证远程传输的高效稳定?

1.3 RPC概念模型

1.3.1 一次RPC的完整过程

image.png

1.3.2 RPC的好处

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

1.3.3 RPC带来的问题

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

2 分层设计

编解码层+协议层+网络通信层

2.1 分层设计样例

2.1.1 APache Thrift

image.png

2.1.2 编解码层

image.png

2.1.3 生成代码

image.png

2.2 编解码层

2.2.1 数据格式

  • 语言特定的格式
  • 文本格式
  • 二进制编码

2.2.2 二进制编码

TLV编码

  • Tag:标签,可以理解为类型
  • Length:长度
  • Value:值,Value也可以是TLV结构

2.2.3 选型

  • 兼容性(支持自动增加新字段,同时不影响老的服务)
  • 通用性(跨平台、语言)
  • 性能(时间和空间)

2.3 协议层

特殊结束符:一个特殊字符作为每个协议单元结束的标识。 image.png

变长协议:以定长加不定长的部分组成,其中定长的部分需要描述不定长的内容长度。 image.png

2.3.1 协议构造

image.png

2.3.2 协议解析

image.png

在框架中,首先会从内存中读取指定的部分,然后根据协议约定,读取MagicNumber可以知道协议的类型,再继续读取编解码方式,由此将PayloadCodec解码出Payload

2.4 网络通信层

2.4.1 网络库

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

3 关键指标

3.1 稳定性

3.1.1 保障机制

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

3.1.2 请求成功率

  • 负载均衡
    • 尽量均匀地调用结点,避免局部某结点负载过大,平衡负载压力,提高请求成功率。
  • 重试
    • 当一些偶然且短暂的故障发生(如网络抖动)导致的请求失败,合理地重试,可增大请求成功率。

3.1.3 长尾请求

长尾请求:响应时间明显高于平均响应时间的小部分请求。

如何提高长尾请求的请求成功率?

  • Backup Request:

image.png 左图为普通的重试;右图为Backup Request,即设定某阈值,若超过该阈值未收到响应则直接再次发送请求,常见的阈值如p99、p95.

3.1.4 注册中间件

image.png

将稳定性策略注入到中间件的开发过程中。

3.2 易用性

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