【分布式】如何理解远程服务调用RPC?

        远程服务将计算机程序的工作范围从单机扩展至网络,从本地延伸至远程,是构建分布式系统的首要基础。//远程服务调用(Remote Procedure Call,RPC)

1、进程间通信

        远程服务调用 RPC 出现的最初目的,就是为了让计算机能够与调用本地方法一样去调用远程方法。下边是一个本地方法的调用示例:

public static void main(String[] args) {
    System.out.println("hello world");
}

        当调用 println() 方法输出 hello world 这行时时,计算机需要处理如下的步骤:

  1. 传递方法参数:将字符串 hello world 的引用地址压栈。
  2. 确定方法版本:根据println()方法的签名,确定其执行版本。但这并不是一个简单的过程,无论是编译时静态解析,还是运行时动态分派,都必须根据某些语言规范中明确定义的原则,找到明确的被调用者,“明确”是指唯一的一个被调用者,或者有严格优先级的多个被调用者,譬如不同的重载版本。
  3. 执行被调方法:从栈中弹出 Parameter 的值或引用,并以此为输入,执行被调用者的内部的逻辑。
  4. 返回执行结果:将调用者的执行结果压栈,并将程序的指令流恢复到 Call Site(调用位置)的下一条指令,继续向下执行。

        试想,如果 println()方法不在当前进程的内存地址空间中会发生什么问题呢?

        不难想到,这样会至少面临两个直接的障碍。

        首先,第一步和第四步所做的传递参数、传回结果都依赖于栈内存,如果Caller(调用者)与Callee(被调用者)分属不同的进程,就不会拥有相同的栈内存,此时将参数在 Caller 进程的内存中压栈,对于 Callee 进程的执行毫无意义。//无法执行方法

        其次,第二步的方法版本选择依赖于语言规则,如果 Caller 与 Callee 不是同一种语言实现的程序,方法版本选择就将是一项模糊的不可知行为。//不兼容版本

        所以,我们又该如何去解决两个进程之间如何交换数据的问题呢?

        以下是解决该问题的几种思路:

        (1)管道(Pipe):管道类似于两个进程间的桥梁,可通过管道在进程间传递少量的字符流或字节流。//例如:ps -ef | grep java

        (2)信号(Signal):信号用于通知目标进程有某种事件发生。

        (3)信号量(Semaphore):信号量用于在两个进程之间同步协作手段,它相当于操作系统提供的一个特殊变量,程序可以在上面进行 wait() 和 notify() 操作。

        (4)消息队列(Message Queue):以上三种方式只适合传递少量消息,POSIX 标准中定义了可用于进程间数据量较多的通信的消息队列。进程可以向队列添加消息,被赋予读权限的进程还可以从队列消费消息。消息队列克服了信号承载信息量少、管道只能用于无格式字节流以及缓冲区大小受限等缺点,但实时性相对受限。

        (5)共享内存(Shared Memory):允许多个进程访问同一块公共内存空间,这是效率最高的进程间通信形式。

        (6)本地套接字接口(IPC Socket):消息队列与共享内存只适合单机多进程间的通信,套接字接口则是更普适的进程间通信机制,可用于不同机器之间的进程通信。套接字(Socket)起初是由 UNIX 系统的 BSD 分支开发出来的,现在已经移植到所有主流的操作系统上。出于效率考虑,当仅限于本机进程间通信时,套接字接口是被优化过的,不会经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等操作,只是简单地将应用层数据从一个进程复制到另一个进程,这种进程间通信方式即本地套接字接口(UNIX Domain Socket),又叫作 IPC Socket。

2、通信成本

        这里只考虑使用本地套接字接口(IPC Socket)进行进程间通信这种方案。这是因为最初计算机科学家们的想法,就是将 RPC 作为 IPC 的一种特例来看待的。

        基于套接字接口的通信方式(IPC Socket),它不仅适用于本地相同机器的不同进程间通信,由于 Socket 是网络栈的统一接口,它也能支持基于网络的跨机进程间通信。譬如Linux系统的图形化界面、X Window服务器和GUI程序之间的交互就是由这套机制来实现的。这样做的好处是,由于Socket是各个操作系统都提供的标准接口,完全有可能把远程方法调用的通信细节隐藏在操作系统底层,从应用层面上来看可以做到远程调用与本地的进程间通信在编码上完全一致。

        问题在于,如果隐藏了远程方法调用的通信细节,就很容易让人忽略了网络的通信问题,造成网络通信无成本的错觉。以下列举了通过网络进行分布式运算的八宗罪:

  • 1)The network is reliable.——网络是可靠的。
  • 2)Latency is zero.——延迟是不存在的。
  • 3)Bandwidth is infinite.——带宽是无限的。
  • 4)The network is secure.——网络是安全的。
  • 5)Topology doesn’t change.——拓扑结构是一成不变的。
  • 6)There is one administrator.——总会有一个管理员。
  • 7)Transport cost is zero.——不必考虑传输成本。
  • 8)The network is homogeneous.——网络都是同质化的。

        所以,如果远程服务调用要透明化,就必须为这些罪过埋单。// RPC的问题

        所以,什么是远程服务调用呢?通过以上分析,我们就能够有更好的理解了

        远程服务调用是指位于互不重合的内存地址空间中的两个程序,在语言层面上,以同步的方式使用带宽有限的信道来传输程序控制信息。//注意是同步方式

3、三个基本问题

        对于所有的RPC协议,都不外乎变着花样使用各种手段来解决以下三个基本问题。

        (1)如何表示数据

        这里的数据包括传递给方法的参数以及方法执行后的返回值

        其实就是如何对数据进行序列化与反序列化。所以每种 RPC 协议都应该要有对应的序列化协议。比如,Java RMI 的Java对象序列化流协议,gRPC的Protocol Buffers,众多轻量级RPC支持的JSON序列化等。

        (2)如何传递数据

        准确地说,是指如何通过网络,在两个服务的 Endpoint 之间相互操作、交换数据。

        这里“交换数据”通常指的是应用层协议,实际传输一般是基于TCP、UDP等标准的传输层协议来完成的。

        两个服务交互不是只扔个序列化数据流来表示参数和结果就行,许多在此之外的信息,譬如异常、超时、安全、认证、授权、事务等,都可能产生双方需要交换信息的需求。在计算机科学中,专门有一个名词“Wire Protocol”来表示这种两个Endpoint之间交换这类数据的行为,常见的Wire Protocol 有://如何交换?需要定义一套规则

  • Java RMI 的 Java 远程消息交换协议 //依赖于特定语言
  • 如果要求足够简单,双方都是 HTTP Endpoint,直接使用 HTTP 协议也是可以的(如JSON-RPC)
        (3)如何表示方法

        每种语言的方法签名都可能有差别,所以“如何表示同一个方法”,“如何找到对应的方法”还是需要一个统一的跨语言的标准才行。这个标准可以非常简单,譬如直接给程序的每个方法都规定一个唯一的、在任何机器上都绝不重复的编号,调用时压根不管它是什么方法、签名是如何定义的,直接传这个编号就能找到对应的方法。这种听起既粗鲁又寒碜的办法,还真的就是 DCE/RPC 当初准备的解决方案。//唯一的绝不重复的编码方案UUID就来源于此

        后来,DCE 还是弄出了一套与语言无关的接口描述语言(Interface Description Language,IDL),成为此后许多 RPC 参考或依赖的基础(如CORBA的OMG IDL)。如今,用于表示方法的协议还有:

  • Android 的 Android 接口定义语言(Android Interface Definition Language,AIDL)
  • CORBA 的 OMG 接口定义语言(OMG Interface Definition Language,OMG IDL)
  • Web Service 的 Web 服务描述语言(Web Service Description Language,WSDL)
  • JSON-RPC 的 JSON Web 服务协议(JSON Web Service Protocol,JSON-WSP)

        总之,以上 RPC 中的三个基本问题,全部都可以在本地方法调用过程中找到对应的解决方案。RPC的设计始于本地方法调用,尽管早已不再追求实现与本地方法调用完全一致的目的,但其设计思路仍然带有本地方法调用的深刻烙印。所以抓住两者间的联系来类比,对我们更深刻地理解 RPC 的本质会很有帮助。

4、统一RPC的困扰

        需要说明的是,同时支持跨系统、跨语言的统一 RPC 设计和使用起来不是非常复杂就是非常繁琐,所以目前并没有一款普适性的统一 RPC。

        面向透明的、简单的 RPC 协议,如 DCE/RPC、DCOM、Java RMI,要么依赖于操作系统,要么依赖于特定语言,总有一些先天约束;那些面向通用的、普适的 RPC 协议,如 CORBA,就无法逃过使用复杂性的困扰,CORBA 烦琐的 OMG IDL、ORB 都是很好的佐证;而那些意图通过技术手段来屏蔽复杂性的 RPC 协议,如 Web Service,又不免受到性能问题的束缚。所以简单、普适、高性能这三点,似乎真的很难同时满足。

5、分裂的RPC

        由于一直没有一个同时满足以上三点的“完美RPC协议”出现,所以远程服务器调用这个小小的领域,逐渐进入群雄混战、百家争鸣的战国时代,距离“统一”越来越远,并一直延续至今。

        现在,已经相继出现过RMI(Sun/Oracle)、Thrift(Facebook/Apache)、Dubbo(阿里巴巴/Apache)、gRPC(Google)、Motan1/2(新浪)、Finagle(Twitter)、brpc(百度/Apache)、.NET Remoting(微软)、Arvo(Hadoop)、JSON-RPC 2.0(公开规范,JSON-RPC工作组)等难以穷举的协议和框架。

        今时今日,任何一款具有生命力的RPC框架,都不再去追求大而全的“完美”,而是以某个具有针对性的特点作为主要的发展方向,举例分析如下:

        (1)朝着面向对象发展,不满足于 RPC 将面向过程的编码方式带到分布式,希望在分布式系统中也能够进行跨进程的面向对象编程,代表为 RMI、.NET Remoting,之前的 CORBA 和 DCOM 也可以归入这类。这种方式有一个别名叫作分布式对象(Distributed Object)。

        (2)朝着性能发展,代表为 gRPC 和 Thrift。决定RPC性能的主要因素有两个:序列化效率和信息密度。序列化效率很好理解,序列化输出结果的容量越小,速度越快,效率自然越高;信息密度则取决于协议中有效负载(Payload)所占总传输数据的比例大小,使用传输协议的层次越高,信息密度就越低,SOAP 使用 XML 拙劣的性能表现就是前车之鉴。gRPC 和 Thrift 都有自己优秀的专有序列化器,而传输协议方面,gRPC 是基于 HTTP/2 的,支持多路复用和 Header 压缩,Thrift 则直接基于传输层的 TCP 协议来实现,省去了应用层协议的额外开销。

        (3)朝着简化发展,代表为 JSON-RPC 牺牲了功能和效率,换来的是协议的简单轻便,接口与格式都更为通用,尤其适合用于Web浏览器这类一般不会有额外协议支持、额外客户端支持的应用场合。

        所以,不同的 RPC 框架所提供的特性或多或少是有矛盾的,很难有某一种框架能满足所有需求。

        若要朝着面向对象发展,就注定不会太简单,如建 Stub、Skeleton 就很烦了,即使由 IDL 生成也很麻烦;功能多起来,协议就会更复杂,效率一般也会受影响;要简单易用,那很多事情就必须遵循约定而不是自行配置;要重视效率,那就需要采用二进制的序列化器和较底层的传输协议,支持的语言范围容易受限。

        也正是每一种RPC框架都有不完美的地方,所以才导致不断有新的 RPC 轮子出现,也决定了
在选择框架时,在获得一些利益的同时,要付出另外一些代价。

        到了最近几年,RPC 框架有明显向更高层次(不仅仅负责调用远程服务,还管理远程服务)与插件化方向发展的趋势,不再追求独立地解决 RPC 的全部三个问题(表示数据、传递数据、表示方法),而是将一部分功能设计成扩展点,让用户自己选择框架聚焦于提供核心的、更高层次的能力,譬如提供负载均衡、服务注册、可观察性等方面的支持。这一类框架的代表有 Facebook 的 Thrift 与阿里的 Dubbo,尤其是断更多年后重启的 Dubbo 表现得更为明显。

        Dubbo 默认有自己的传输协议(Dubbo协议),同时也支持其他协议;默认采用 Hessian 2 作为序列化器,如果你有 JSON 的需求,可以替换为 Fastjson,如果你对性能有更高的追求,可以替换为 Kryo、FST、Protocol Buffers 等效率更好的序列化器,如果你不想依赖其他组件库,也可以直接使用 JDK 自带的序列化器。这种设计在一定程度上缓和了 RPC 框架必须取舍、难以完美的缺憾。

        关于 Dubbo 的相关内容,我将在其他文章中进行介绍,至此,全文结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/900213.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

K8S如何基于Istio重新实现微服务

K8S如何基于Istio重新实现微服务 认识 Istio前言Istio 的理念Istio 的架构数据平面控制平面服务与 Istio 的关系 Istio 实践环境准备安装 Helm安装Istio 使用 Istio 代理运行应用情感分析应用的架构使用 Istio 代理运行应用Sidecar 注入Ingress 网关网关资源VirtualService 资源…

离线部署jdk8,jdk17, jdk21

目录 一、下载地址二、环境三、离线部署思路步骤 四、部署脚本 在开发过程中,有时我们需要在同一台机器上安装多个不同版本的 JDK,以满足不同项目的需求。本文将详细介绍如何在 Ubuntu 24.04 中离线安装 JDK8、JDK17 和 JDK21。 一、下载地址 建议下载O…

融合DevOps打造企业高效流程体系的实践与探索

一、引言 转眼间,我已毕业十多年,在IT领域深耕不辍,曾涉足全栈研发、大数据研发、架构设计与项目管理等多个岗位,更主导过公司从市场到交付再到运营的全链条流程建设。在这漫长的职业生涯中,一个问题始终萦绕在我心头&…

web前端练习

01 代码&#xff1a;HTML部分 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>search</title>…

华为配置BFD状态与接口状态联动实验

组网图形 图1 配置BFD状态与接口状态联动组网图 BFD简介配置注意事项组网需求配置思路操作步骤配置文件 BFD简介 为了减小设备故障对业务的影响&#xff0c;提高网络的可靠性&#xff0c;网络设备需要能够尽快检测到与相邻设备间的通信故障&#xff0c;以便及时采取措施&…

百科知识|选购指南

百科知识||选购指南 百科知识选购指南茶叶分类茶叶的味道来源茶叶制作步骤名茶其他一些茶叶的知识 百科知识 选购指南 茶叶 分类 茶叶种类: 六大茶类完美分析介绍&#xff01;茶友推荐收藏 (aboxtik.com) 1.绿茶&#xff08;发酵率0%&#xff09; 2.白茶&#xff08;发酵率…

对杨笠没有意见,但对京东有 | 生活周刊 #5

持续分享高效率工具&#xff0c;以及日常生活、个人成长、运动健康和法律案例等的内容&#xff0c;欢迎关注 &#x1f4f6; 关于杨笠 对于杨笠&#xff0c;我对她没什么偏见&#xff0c;也好奇为什么代言京东会触发这么大的反弹&#xff0c;直到我看到杨笠的冒犯的艺术&#x…

Axure垂直菜单展开与折叠

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;Axure垂直菜单展开与折叠 主要内容&#xff1a;垂直菜单单击实现展开/折叠&#xff0c;点击各菜单项显示选中效果 应用场景&#xff1a;后台菜单设…

【python实战】利用代理ip爬取Alibaba海外版数据

引言 在跨境电商的业务场景中&#xff0c;数据采集是分析市场、了解竞争对手以及优化经营策略的重要环节。然而&#xff0c;随着越来越多企业依赖数据驱动决策&#xff0c;许多跨境电商平台为了保护自身数据&#xff0c;采取了更严格的防护措施。这些平台通过屏蔽大陆IP地址或部…

专业135+总分400+西安交通大学815869(原909)信号与系统考研经验电子信息与通信工程,真题,大纲,参考书

经过将近一年的考研复习&#xff0c;终于梦圆西安交大&#xff0c;今年专业课815(和专硕869&#xff08;原909&#xff09;差不多)信号与系统135&#xff0c;总分400&#xff0c;回想这一年的复习还有很多经验和大家分享&#xff0c;希望可以对大家复习有所帮助&#xff0c;少走…

Docker 与 Yocto

Yocto项目为什么需要Docker Yocto 项目并不直接依赖 Docker&#xff0c;但在某些情况下使用 Docker 可以为 Yocto 项目提供以下具体且实际的好处&#xff1a; 1. 环境一致性&#xff1a; Yocto 构建需要一个稳定且一致的开发环境。不同的 Linux 发行版可能会有不同的库版本、…

如何实现智能图像擦除

我们在拍照时&#xff0c;往往会拍到一些路人或者杂物&#xff0c;但是这个照片又不想删掉&#xff0c;那么有没有啥方法可以把照片中的特定部分给删除&#xff0c;然后还原这部分的原始内容呢&#xff1f;有些人可能会用ps&#xff0c;但是ps操作比较复杂并且效果还不是很好&a…

Photoshop中的混合模式公式详解

图层混合简介 图层混合&#xff08;blend&#xff09;顾名思义&#xff0c;就是把两个图层混合成一个。 最基本的混合是alpha融合&#xff08;alpha compositing&#xff09;&#xff0c;这是一个遵循光的反射与透射等&#xff08;简化版&#xff09;物理学原理的混合方式。 各…

信号与系统学习:傅里叶级数

一、基本概念 1. 什么是傅里叶级数&#xff1f; 傅里叶级数是一种数学工具&#xff0c;可以将一个周期函数分解为一系列正弦和余弦函数&#xff08;即三角函数&#xff09;的和。这些正弦和余弦函数的频率是原函数的整数倍。 2. 为什么要使用傅里叶级数&#xff1f; 信号分…

J2学习打卡

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 ResNet50V2 import torch import torch.nn as nn import torch.nn.functional as Fclass Bottleneck(nn.Module):expansion 4def __init__(self, inplanes,…

软考(中级-软件设计师)计算机系统篇(1024)

#1024程序员节|正文# 六、树和二叉树 6.1 树的基本概念 描述结果结点的度子结点的个数树的度最大结点的度叶子结点没有子结点的结点内部结点除根结点和叶子结点外的结点父节点有子结点的结点子节点有父结点的结点兄弟节点有同一个父结点的结点层次4层 6.2 二叉树的基本概念…

AI时代LabVIEW程序员的未来出路

随着GPT等AI技术的迅速发展&#xff0c;AI已经能够自动完成大量的代码生成工作&#xff0c;这无疑给LabVIEW程序员带来了新的挑战和机遇。尽管AI能够替代部分编程工作&#xff0c;LabVIEW程序员依然可以通过以下几方面找到出路&#xff1a; 复杂系统集成&#xff1a; AI可以帮助…

【软考高级架构】关于分布式数据库缓存redis的知识要点汇总

一.分布式数据库的含义 分布式数据库缓存指的是在高并发的环境下&#xff0c;为了减轻数据库的压力和提高系统响应时间&#xff0c;在数据库系统和应用系统之间增加一个独立缓存系统。 二.常见的缓存技术 &#xff08;1&#xff09;MemCache: Memcache是一个高性能的分布式的内…

你对MySQL的having关键字了解多少?

在MySQL中&#xff0c;HAVING子句用于在数据分组并计算聚合函数之后&#xff0c;对结果进行进一步的过滤。它通常与GROUP BY子句一起使用&#xff0c;以根据指定的条件过滤分组。HAVING子句的作用类似于WHERE子句&#xff0c;但WHERE子句是在数据被聚合之前进行过滤&#xff0c…

闯关leetcode——205. Isomorphic Strings

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/isomorphic-strings/ 内容 Given two strings s and t, determine if they are isomorphic. Two strings s and t are isomorphic if the characters in s can be replaced to get t. All occur…