并行系统 Parallelism System(二)
远程过程调用 Remote Procedure Call RPC
- 目标: 让调用远程计算机上的程序就像调用本地计算机上的程序一样简单和透明
- 机制:当使用RPC时,编写的代码看起来想调用一个普通的本地函数(procedure), 实际上,这个函数的执行是在另外一台计算机(remote)上进行的
- 隐藏的细节:RPC框架会负责处理所有复杂的网络通信细节,如:数据的序列化、网络传输、错误处理等
eg:
- 一个函数
add(a, b)
,它在服务器 A 上。在本地机器 B 上 - 本来需要写代码来建立网络连接、发送数据、等待响应、解析数据才能调用服务器 A 上的
add
函数 - 机器 B 上可以直接写:
result = add(5, 3)
。RPC 框架会自动把这个调用“转换”成网络请求发送给服务器 A,服务器 A 执行add
函数,然后把结果返回 - RPC 框架再把结果“转换”成你本地的变量
result
为什么需要RPC? Why do we need RPC?
-
为什么不能直接使用本地函数调用?因为本地函数调用和远程函数调用本质的区别
进程空间不同:
- 本地调用: 被调用的函数和调用者在同一个进程的内存空间内,可以直接访问共享内存和数据
- 远程调用: 被调用的函数在另一个独立的进程(通常在另一台机器上)中,它们有各自独立的内存空间,不能直接共享数据。数据必须通过网络传输
网络通信复杂性:
- 本地调用: 不需要网络
- 远程调用: 涉及到复杂的网络通信:
- 查找服务: 调用方需要知道服务在哪个 IP 地址和端口上
- 数据序列化/反序列化: 调用参数需要从内存中的数据结构转换成字节流,通过网络发送;接收方收到字节流后再转换回数据结构
- 网络传输: 使用 TCP/IP 等协议进行可靠的数据传输
- 错误处理: 网络可能会断开、消息可能会丢失或延迟,服务器可能会崩溃。这些都需要妥善处理
- 阻塞/非阻塞: 调用方可能需要等待远程调用的结果,或者异步地处理结果
- 异构性:
- 分布式系统中,不同的服务可能使用不同的编程语言、操作系统或硬件平台
- RPC 框架需要能够处理这些异构性,使得不同技术栈的服务能够互相通信
RPC的核心价值是什么?
- 抽象了网络通信的复杂性。开发者可以像编写本地函数一样编写远程服务调用,而不用关心底层 Socket 编程、协议设计等
- 封装了数据序列化、网络传输、错误处理等细节。这使得开发者能够专注于业务逻辑,大大提高了开发效率
- 促进了模块化和解耦。不同的服务可以独立开发、部署和升级,只要它们的 RPC 接口保持不变,就能互相通
RPC是如何工作的? (How RPC Works)
- 客户端存根 (Client Stub) / 代理 (Proxy)
- 服务端存根 (Server Stub) / 骨架 (Skeleton)
- RPC 运行时系统 (RPC Runtime System)
- 数据序列化 (Serialization) 与反序列化 (Deserialization)
- 网络传输 (Network Transport)
eg:(客户端程序)调用一个远程函数时:
- 实际调用的是本地的客户端存根
- 客户端存根负责将调用的函数名、参数等信息序列化成字节流
- 客户端存根通过 RPC 运行时系统将这些字节流发送到服务器
- 服务器端的 RPC 运行时系统接收到字节流后,将其传递给服务端存根
- 服务端存根将字节流反序列化成服务器程序能理解的数据结构和参数
- 服务端存根调用服务器上真正的函数来执行操作
- 函数执行完成后,结果被序列化
- 结果通过服务器端的 RPC 运行时系统传回客户端
- 客户端的 RPC 运行时系统接收到结果,由客户端存根进行反序列化
- 客户端程序最终收到调用的结果
RPC 与本地函数调用的根本区别
- 本地调用: 要么成功,要么整个程序崩溃
- RPC: 远程调用可能失败,但客户端和服务器都可能继续运行。更复杂的是,失败的原因可能有很多种:
- 服务器宕机: 服务器在处理请求前、处理中或处理后崩溃
- 网络问题: 请求消息丢失、响应消息丢失、网络分区、延迟过高
- 客户端宕机: 客户端在发出请求后、收到响应前崩溃
- 超时: 客户端等了太久没有收到响应,于是决定放弃
- 不确定性: 客户端很难区分一个没有收到响应的请求是因为服务器没收到、服务器处理失败了、还是服务器处理成功但响应在路上丢失了
- 本地调用: 函数调用几乎没有延迟(微秒甚至纳秒级别),而且数据传输速度极快(内存访问速度)
- RPC: 涉及到网络传输,即使在局域网内,延迟也通常在毫秒级别,广域网则更高。带宽也有限
- 参数传递方式不同 (Different Parameter Passing):
- 本地调用: 可以通过引用(指针)传递复杂的数据结构,共享内存
- RPC: 只能通过值传递参数。所有参数都必须被序列化成字节流进行传输。这意味着传递大量数据会消耗更多时间和网络带宽
- 异构性 (Heterogeneity):
- 本地调用: 调用方和被调用方通常在相同的操作系统和编程语言环境下
- RPC: 客户端和服务器可能运行在不同的操作系统上,使用不同的编程语言,甚至有不同的硬件体系结构。RPC 框架必须处理这些差异
- 安全性 (Security):
- 本地调用: 通常认为在同一个进程内是安全的
- RPC: 请求通过网络传输,容易受到监听、篡改、伪造等攻击。因此,安全性(加密、认证、授权)在 RPC 中变得非常重要