青训营 RPC 框架分层设计 1

75 阅读4分钟

简介

RPC 全称为 Remote Procedure Call 即远程过程调用,我们先对比一下两种不同函数调用方式的区别。

函数调用

本地函数调用

image.png

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

image.png

  • 网上商城和支付服务不是部署在同一个网络上;(地址空间不同)
  • 解决函数映射问题,如何识别对方的指定函数(id)
  • 如何将参数告诉对方(不能通过共享内存传递,需要客户端将参数转换成字节流,然后服务端将字节流转换成对应的参数结构体)
  • 网络传输

RPC 问题

  • Heterogeneity(异质性)

    • 客户端需要与服务器会合
    • 服务器必须分派到所需的功能
      • 如果服务器是不同类型的机器怎么办?
  • Failure

    • 如果消息丢失怎么办?
    • 如果客户端、服务器或网络出现故障怎么办?
  • 性能

    • 过程调用需要 ≈ 10 个周期 ≈ 3 ns
    • 数据中心中的 RPC 耗时约 10 μs(慢 103 倍)
    • 在大范围内,通常速度慢10^6倍

数据传输问题

对于远程过程调用,远程计算机可能:

  1. 使用不同大小表示数据类型
  2. 使用不同的字节顺序(字节序)
  3. 以不同方式表示浮点数
  4. 有不同的数据对齐要求(4 字节类型仅从 4 字节内存边界开始)
  • 字节序(大端小端)

大端就是从数据的高位字节开始存储(即高位字节存储到低位地址处,符合人类的阅读习惯)

小端是从数据的低位字节开始存储(即低位字节存储到低位地址处,符合计算机的阅读习惯)

image.png

编程支持方面的差异

许多编程语言没有远程过程调用的内置概念(c、c++、java),某些语言支持启用 RPC(python、haskell、go)。

问题解决办法:IDL:Interface Description Language

以与机器无关的方式传递过程参数和返回值的机制。

  • 程序员可在 IDL 中编写接口描述:

    • 定义过程调用的 API:服务名称、参数/返回类型
  • 然后运行 IDL 编译器,生成:

    • 将本地数据类型转换为(序列化)独立于机器的字节流的代码
    • 反之亦然,称为反序列化
  • client stub :将本地过程调用作为请求转发到服务器

  • server stub:将RPC分派到它的实现

为什么需要 RPC?

  • 典型的程序员接受的培训是编写在一个地方运行的单线程代码

  • 目标:易于编程的网络通信,使客户端 - 服务器通信透明

    • 保留编写集中式代码的“感觉”(程序员无需考虑网络问题)
  • 分布式编程具有挑战性

    • 需要通用的原语/抽象来隐藏复杂性
    • 例如,用于隐藏块布局的文件系统抽象、用于调度/故障隔离的进程抽象
  • 在单个程序内,在单个进程中运行时,可以回顾一下众所周知的过程调用概念:

    • 调用者将参数压入堆栈,

      • 跳转到被调用函数的地址
    • 被调用者从堆栈中读取参数,

      • 执行,将返回值放入寄存器中,
      • 返回调用者的下一条指令

RPC’s Goal: To make communication appear like a local procedure call: transparency for procedure calls

RPC 的目标:使通信看起来像本地过程调用:过程调用的透明度

Example

image.png

RPC 概念模型

image.png

User 发起本地调用,user-stub 将参数打包,打包完成之后交给 rpcruntime; rpcruntime 将数据传送给对端,接收完成之后,服务端的 rpcruntime 将其发送给 server-stub 进行解包,使用解包得到的参数调用 Server 提供的服务,Server 处理完成后,也要将结果进行打包,打包完成后经 rpcruntime 负责发送到对端,对端接收后解包获取服务端执行结果,然后返回给调用方。(调用方感觉就像本地调用一样,对远程调用无感知)

一次 RPC 的完整过程

image.png

这部分都是上课的笔记,下部分是自己整理的关于 RPC 的学习内容。