简谈分布式计算和本地计算的区别

版权归作者所有,任何形式转载请联系作者。
作者:tison(来自豆瓣)
来源:https://www.douban.com/note/720831988/

古老的论文《A Note on Distributed Computing》介绍了分布式计算(Distributed Computing)和本地计算(Local Computing)的四点主要的差别,批评了远程方法调用(Remote Method Invocation)对区分不同计算类型的无力,提出了要明确区别分布式计算与本地计算的观点。

这篇论文出产与 1994 年,当时正是 RPC 和 RMI 大行其道的时候。软件开发者试图以一个模型统一所有的开发,提出将面向对象扩展到分布式的计算环境中,使得对象访问无需区分对象是一个本地对象或是远程对象。这种 Slogan 乍一听很有诱惑力,但是不同于 Truffle Framework 的通用访问对不同语言的对象访问的遮蔽,本地和远程对象的访问机制远远不是不同语言带来的语义差异,还有更基础的差别,使得试图遮蔽本地方法调用和远程方法调用会掩盖一些分布式系统固有的问题。

其之一,是调用延迟(Latency)的问题。这一点是显而易见的,因为调用一个本地的方法不需要走网络,而访问一个远程的对象需要进行网络通信。这使得代码中相似的两个方法调用其性能分析可能是大不相同的,而这种差异很难体现在代码层面上。然而,延迟的问题只是一种表征,我们还是可以期望硬件的升级能带来性能的进步。延迟问题并不会导致更严重的程序正确性问题。

其之二,是内存访问(Memory Access)的差异。这一点在点破之后也是显而易见的,即一个本地的引用传输到远端进程上,这个引用基本上就是无效的。一个现代的例子是 Akka 框架下的 ActorRef,本地的引用在序列化时转化为对应的路径,在远端进程中根据具体的 ActorSystem 环境再次由 ActorProvider 从路径中产生。传递引用的无效性是因为引用通常与本地的状态,尤其是地址空间相关。一个与本地状态关联,指向本地对象的引用,在远端进程中根本是无意义的。这一点首先启发我们应该在网络通信中传递值信息,同时也说明了要在应用层面遮蔽引用的不同需要额外的工作。如果不加区分的将这种工作应用在本地对象和远程对象上,那么本地对象的访问开销就会有不必要的部分。

其之三,是部分失败(Partial Failure)的问题,即由于分布式计算没有一个统一的失败机制与资源管理器(在本地环境下,可以是操作系统),计算的失败是难以察觉、辨明和恢复的。典型的场景是进程 A 请求进程 B 执行一个动作,如果进程 A 收到进程 B 的回复,那么它可以知道动作已经完成。但是如果进程 A 没有收到进程 B 的回复,那么进程 A 是不知道这是由于自己的信息没有发送过去,还是进程 B 执行时失败,还是进程 B 完成动作后的反馈丢失的。这种问题是客观存在的,试图遮蔽它,如果是全部认为是本地调用,那么系统将会在部分失败发生时卡死或宕机;如果全部认为是可能发生部分失败的远程调用,那么对于本地调用仍然有不可避免的开销。

其之四,是并发(Concurrency)的问题。分布式计算中的远程调用潜在的是并发的,同样的,不加区分的对本地方法调用和远程方法调用统一建模,如果忽视并发性,则一个远程方法调用可能会永远不返回;如果全部假设为潜在并发的,则本地方法调用会带来额外的复杂性。

综上所述,问题并不在于性能下降或者程序失败,而在于强调必须正视分布式计算与本地计算在模型上的差别,区分不同的场景以对不同的需求进行适当的权衡与建模。

关于第二点,其中讲到的 Akka 的例子也已经很清楚了,同样的努力还发生在 gRPC 等框架中。对于远程方法调用产生的引用失效问题,最关键的点是程序员需要对它有认知。我在处理 FLINK-11603 的时候,一开始试图将一个本地的 Gateway 直接序列化传送到远程,这实际上是一个裸地发送 ActorRef 的行为。后来在了解到引用失效的问题之后,改为传送路径名的值对象,在远端根据这个值重新建立起连接并获得 Gateway。

关于第三点,可以参考这篇文章《Nobody Needs Reliable Messaging》(https://www.infoq.com/articles/no-reliable-messaging/)。把远程调用【就这么】认为是本地调用会导致严重的正确性问题,首先是需要区分本地调用和远程调用。但是对于远程调用我们选择提供何种程度的可靠性,这仍然是一个值得探讨的问题。

关于第四点,并发是新时代软件开发者怎么也绕不过去的议题。本地调用中的 IO 操作也可能需要并发来减少阻塞,甚至单纯的计算操作也可以通过并行发送到不同的核同时计算以提高性能。当然,对于一些不存在并发的简单调用,我们不需要引入并发上下文。这一点仍然是要求软件开发者对何时应该并发要有一个准确的认识。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章