NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

分布式计算编程模型之 RPC

  • 2016-04-28
  • 本文字数:3699 字

    阅读完需:约 12 分钟

远程过程调用(RPC)范式的出现可以追溯到 40 年之前。时至今日,它仍是在编写分布式应用时使用率最高的一种编程模型。只是近些年来,人们对于 RPC 技术的质疑与批评声逐渐多了起来。Steve Vinoski 在 2008 年曾尖锐地指出,之所以 RPC 仍然能够得到诸多开发者的支持,其原因只有一个:舒适感!Vinoski 完全不认可这种思想,他表示:

“开发者的舒适感真的比正确性、可伸缩性、性能、关注分离、可扩展性以及附加的复杂性还要重要吗?”

尽管面临着这些尖锐的批评,但 RPC 的历史地位是不容置疑的,而它在现代化的应用中仍能够占据一席之地,成为分布式计算中一种重要的编程模型。正在攻读博士学位的 Christopher Meiklejohn 近来开设了一系列博客文章以回顾分布式计算中的各种编程模型与语言,在其中一篇文章中对 RPC 进行了详尽的回顾与展望。

概述

简单来说,一台机器上的程序对另一台机器上的子程序的调用就是一次 RPC 调用。在调用过程中,主程序不需要操心与远程执行相关的任何代码,与本地调用相比,其唯一区别就在于需要提供远程节点的标识。最早为人所知并接受的 RPC 实现是由 Sun 提供的 SunRPC 机制,使用在其网络文件系统(NFS)中。

除此之外,常见的 RPC 机制还包括 Java 的 RMI、DCOM、XML-RPC、SOAP、CORBA,以及 Google 的 gRPC 等等。

RPC 的早期发展

RPC 思想最早的原型可追溯至 1974 年所发布的 RFC 674 草案 —— “过程调用协议文档第 2 版”,该草案当时的目标是为因特网上的全部 70 个节点定义一种共享资源的通用方式,在该草案中引入了过程调用范围第 2 版(PCP)的概念。而在第二年发布的 RFC 684 草案 —— “对以过程调用作为网络协议的评论”中,首次分析了 RPC 这种编程范式存在的三大问题以及这些问题与分布式系统的本质问题之间的关联。这三大问题可以简要地概述如下:

  • 过程调用通常是一种命令式操作,而命令式操作通常是一种来自底层抽象的非常快速的上下文切换操作。
  • 本地调用与远程调用的不同之处在于:远程调用可能会产生延迟,甚至在产生故障时可能永远也不会返回.
  • 异步的消息传递,或是发送某个消息并等待响应是一种更理想的模型,因为这会使消息的传递变得更加明确。

伴随着这三大问题的是使用这种编程范式时的一系列麻烦,这些麻烦在 RPC 的 40 多年发展历史中始终阴魂不散,包括:如何从故障或错误中恢复;如何始终保证操作的正确顺序;RPC 范式强制使用者进行同步编程方式;RPC 的调用-响应模型使得因系统过载而导致消息无法正常处理时,对优先级的排列变得相当困难。

随后发布的 RFC 707 草案继承了 RFC 684 的思想,并提出了一个新问题,即各种服务,例如 TELNET 与 FTP 之间的资源共享问题。因为这些服务各自具有不同的接口,因此使用者必须了解他们的操作端口与命令。该草案的作者提出了一个建议:为远程过程的执行定义一个通用的接口,该接口接受一个参数列表,并依然遵循 RPC 的调用-响应模型。虽然这一提议并未解决 RPC 684 中所提出的问题,但这一模型在之后依然得到了许多系统的采纳。

CORBA

CORBA 是对面向对象语言的一种抽象,允许开发者进行跨机器、跨语言的通信。CORBA 通过接口定义语言(IDL)指定远程对象的接口。IDL 用于生成远程系统中的对象接口在本机中的桩代码,并且在实际的语言实现与抽象接口之间生成映射关系。

CORBA 的试图为应用开发者带来几点益处:不依赖于具体的语言、操作系统以及架构;将 IDL 中的抽象类型映射为具体实现所带来的静态类型特性;以及对象在不同机器之间的传输。CORBA 承诺,通过使用映射,远程方法调用的使用就与本地调用一样简单,甚至与分布式系统相关的异步也可被映射为本地异常进行处理。

但是,Vinoski 在 2003 年表示,仅基于透明性这一点对于编程语言与抽象进行评估的方式是有缺陷的。在他看来,IDL 映射的目的在于将中间件抽象直接合并至编程语言的领域中,通过这种透明性减少编程语言与中间件这两者之间的阻抗失调。但问题在于,不恰当的透明性可能会掩盖分布式计算中出现的某些问题,例如并发与部分失败相关的问题。

对RPC 范式的批评

Tanenbaum 与 van Renesse 对 RPC 范式提出了尖锐的批评,他们认为将远程调用与本地调用一视同仁的思想在本质上就是错的,RPC 试图打造的透明性也是根本不可能实现的。他们认为为远程访问专门设计一种协议是更好的做法。

Tanenbaum 与 van Renesse 的批评意见涵盖了 RFC 684 草案中已经提到的几点内容:延迟、缺乏并行性、异常处理以及故障检测等等。此外,他们还提出了一些批评意见:

单线程服务器

如果服务器无法立即向客户端发送响应,比如它正在等待来自另一台服务器的输入。在这种情况下,不仅服务器端产生了阻塞,客户端也无法继续执行本地计算过程。

两军问题

怎样才能让两台服务器对于某个 RPC 的成功执行以及收到响应的结果达成一致呢?虽然某一方可以向对方发送确认信息,但对方还得向这个确认信息发送另一个确认信息以再次确认。因此无论发送几次确认都无法实现 100% 的一致性。这一主题其实也是一致性问题的核心,许多与分布式系统相关的文献对其进行了更深入的探讨。

参数

Tanenbaum 与 van Renesse 也叙述了参数传递与参数封送的问题,这一问题在 CORBA 等有可能包含引用的对象系统中显得更为严重。在这种情况下,为了保证引用的有效性,必须使用某种特定的分布式引用。

幂等性

最后一个问题是如何跨网络表达只执行一次的语义,作者在此处强调了幂等性(idempotence)的重要性。简单来说,具有幂等性的操作即使经过多次执行,其结果与只执行一次也没有区别。举例来说,HTTP 中的 PUT 就具有幂等性的语义,而 POST 则不具有这一语义。作者提到了一个可能发生的场景:假设服务器在完成某个操作之后突然崩溃而来不及发送确认信息,客户端就有可能在超时之后再次发送这个实际上已经完成的请求,如果此时服务器完成了重启,就有可能再次执行这一操作。而如果该操作不满足幂等性,就可能产生一些意外的副作用。

分布式计算备忘录

Jim Waldo 和 Sam Kendall 等人共同撰写了一篇非常有名的论文“分布式计算备忘录”,这篇论文在 Reddit 上被人推荐为“每个程序员都应当至少读上两篇”的论文。在这篇论文中,作者表示“忽略本地计算与分布式计算之间的区别是一种危险的思想”,特别指出了 Emerald、Argus、DCOM 以及 CORBA 的设计问题。作者将这些设计问题归纳为“三个错误的原则”:

  1. “对于某个应用来说,无论它的部署环境如何,总有一种单一的、自然的面向对象设计可以符合其需求。”
  2. “故障与性能问题与某个应用的组件实现直接相关,在最初的设计中无需考虑这些问题。”
  3. “对象的接口与使用对象的上下文无关”

十年一轮回的错误

Waldo 表示,每过 10 年,人们就会再次尝试将本地计算与远程计算的设计揉合在一起,再一次犯下相同的错误。他再次强调:本地计算与远程计算的本质是完全不同的。

延迟

最明显的区别就在于延迟问题:如果忽略了延迟问题,软件的性能就会受到直接影响。Waldo 表示,“依赖于底层硬件速度的逐步提高”是错误的,一些实际的问题是很难通过测试找出的。性能分析是一个复杂的问题,在某一时刻表现良好的设计未必永远是合适的。

内存访问

Waldo 对内存访问的批评是特定于 CORBA 与它的继任者的:对象可能会引用在同一地址空间内的指针,但一旦对象产生了移动,这些指针就会变得无效化。他认为处理这一问题的一种途径是使用分布式共享内存,但在实践上更常见的做法是使用封送或 CORBA 引用替换技术。

局部故障

作者在最后谈到了一个最本质的问题:局部故障。在本地计算中,故障都是可检测的。而在分布式计算中,相互独立的组件可能会产生故障,并且故障可能是局部的。

舒适感胜于正确性

在文章的开头部分曾经提到了 Vinoski 对于 RPC 的批评,他认为选择 RPC 的唯一原因在于开发者的舒适感。在提出这一说法几年之后,他提出了几个非常重要的论点:

  • IDL 的阻抗失调:对基础类型进行映射可能比较简单,但复杂的类型是非常难以映射的。
  • 可伸缩性:RPC 范式本身并没有对缓冲提供任何支持,或是提出任何缓解高延迟的机制,它仍然以一种偏命令式的操作构建分布式应用。
  • REST:REST 本身是一种很好的思想,它为管理分布式资源的问题提出了特别的应对方式。但大多数基于 REST 打造的框架都改变了这一抽象思想,仍然重复了这一问题。

分布式编程语言

当我们在谈到分布式编程语言时,多数开发者所想到的其实只是如何用一般性的编程语言去构建分布式系统。实际上,只要某种语言支持并发元素,并且能够打开一个网络套接字,那么就能够构建一个分布式系统。而真正的分布式编程语言为分布式特性提供了第一等的支持。像 Go 这样的语言更像是一种并发语言,它为并发提供了第一等的支持。虽然并发是分布式中的一个重要部分,但他们毕竟还是不同的主题。

Erlang 则为分布式提供了第一等的支持,它虽然同样使用了 RPC 机制,但更倾向于在进程之间使用异步消息传递方式。受到这一设计优秀表达能力的激励, Distributed Process Akka 等框架也随之出现,以提供 Erlang 风格的语义能力。


感谢杜小芳对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016-04-28 19:0013808
用户头像

发布了 428 篇内容, 共 172.1 次阅读, 收获喜欢 38 次。

关注

评论

发布
暂无评论
发现更多内容

拍卖直播APP开发公司选择,延期赔付合同承诺无隐藏费用

软件开发-梦幻运营部

左耳听风 - 高效沟通「读书打卡 day 09」

Java 工程师蔡姬

读书笔记 程序员 个人成长 沟通 职业发展

修复uni-simple-router@2.0.7版本query参数为null时的bug

达摩

uni-app Vue uni-simple-router

重磅发布!基于百度飞桨的《人工智能基础及应用》书籍正式上线

飞桨PaddlePaddle

人工智能 机器学习 深度学习 百度飞桨

阿里巴巴按关键字搜索商品API(alibaba.item_search):如何保证阿里巴巴按关键字搜索商品API返回的商品信息的准确性

技术冰糖葫芦

API 编排

申请开启|成为亚马逊云科技 Community Builder,共建云端社区!

亚马逊云科技 (Amazon Web Services)

数据库

vivo智能活动中台-悟空系统建设之路

vivo互联网技术

低代码 活动中台 AIGC技术探索

2024-01-17:lc的30. 串联所有单词的子串

福大大架构师每日一题

福大大架构师每日一题

相比于 Jira,极狐GitLab 的敏捷项目管理是怎样的?

极狐GitLab

云原生场景下,AIGC 模型服务的工程挑战和应对

阿里巴巴云原生

阿里云 云原生

如何在 Ubuntu / Raspbian 上安装 PostgreSQL

HoneyMoose

在线白板软件哪个好?2024年不要错过这7款高效神器!

彭宏豪95

效率工具 在线白板 在线协作 效率软件 boardmix

C-Shopping基于Next.js,开源电商平台全新亮相

Geek_9da61c

nodejs nextjs Redux Tailwind reactjs

流式传输 VR 和 AR 内容——实时云渲染服务

3DCAT实时渲染

实时渲染 实时云渲染 云流化

IT行业,抢滩大模型

脑极体

AI IT

电商通讯服务

ctsxiyou

通信 通讯

行云部署前端架构解析-前言 | 京东云技术团队

京东科技开发者

青否数字人源码一套到底需要多少钱?

青否数字人

数字人

2023 IoTDB Summit:北京城建智控科技股份有限公司高级研发主管刘喆《IoTDB 在城市轨道交通综合监控系统中的应用》

Apache IoTDB

掌握 C# 变量:在代码中声明、初始化和使用不同类型的综合指南

小万哥

C# .net 程序人生 软件工程 后端开发

淘宝商品列表数据怎么导出

tbapi

淘宝电商 淘宝商品数据接口 taobao agent system

发现一款提高工作效率的利器——ONLYOFFICE办公软件

袁袁袁袁满

人工智能 Office 自动化办公

如何做标准化?| 京东云技术团队

京东科技开发者

使用Docker安装MongoDB数据库

emanjusaka

mongodb Docker 迁移

低代码与智能化办公:解锁工作效率的新纪元

不在线第一只蜗牛

人工智能 低代码 数字化 智能化 智能办公

通过python封装1688商品跨境属性API接口文档

tbapi

Python爬虫 1688API 1688商品跨境属性接口

典型场景解析|PolarDB分布式版如何支撑SaaS多租户?

阿里云瑶池数据库

数据库 阿里云 SaaS 多租户

网络安全中的人工智能:保护未来数字世界的利剑

快乐非自愿限量之名

人工智能 低代码 AI技术 智能化

了解一下Java21的VirtualThread

袁世超

虚拟线程 Java21

强化加密生态合规性:Token Explorer 助你一臂之力!

Footprint Analytics

区块链 Token

模型服务网格:云原生下的模型服务管理

阿里巴巴云原生

阿里云 云原生 服务网格

分布式计算编程模型之RPC_语言 & 开发_邵思华_InfoQ精选文章