`
阅读更多
A Note on Distributed Computing
读了英文paper,选择自认为重要的部分是翻译出来,有兴趣的同学们还是要读原文。

分布式计算的读书笔记
2. 统一对象
在分布式面向对象的计算领域,有一种愿景,希望从程序员的角度来看,在同一地址空间中的对象和在不同机器上的对象之间没有本质区别。这样程序员在设计和实现的时候就可以不用考虑对象是本地还是远程的,以统一的方式来对待本地和远程对象,由底层机制来处理远程对象调用。
这种愿景是远程过程调用(RPC)目标的扩展。这种愿景意味着系统是“objects all
the way down”。在这样的系统中,对象和通讯遵守单一的范式,不用考虑对象的位置。
要实现这样的模型,需要统一地址空间的技术例如Microsoft’s Object Linking
and Embedding或者网络RPC。
在这种模型下编写分布式应用分三个阶段。
第一阶段编写程序不考虑对象位置以及对象如何通信。由系统来选择对象的缺省位置。这种方法可以强迫程序员分离应用的抽象架构与性能调优。
第二阶段通过确定对象位置以及通信方法来调整性能。这需要工具来分析对象之间的通信模式,理论上是可以开发出这样的工具的。
第三阶段是进行实际测试。增加复制,事务等功能来处理分布式带来的局部失败。
这种愿景认为一个调用是本地的还是远程的不影响程序的正确性。
这样的系统有很多优势。它改变了软件维护的方式。改变或者升级的粒度,从整个系统级别变为单独的对象级别。只要对象之间的接口不变,对象的实现可以任意改变。可以把远程服务移动到本地地址空间,也可以把共享同一地址空间的对象分散到不同机器上。可以修复一个对象并安装该对象而不用担心会影响其他对象。
这种愿景包含以下原则:
1、对于一个应用有一个自然的面向对象的设计方案,不管该应用部署在什么环境。
2、 失败和性能问题与组件的实现相关,应该在初始设计之后再考虑这些问题。
3、 对象的接口不依赖于使用对象的环境。

不幸的是,这些原则都是错误的。

4 本地和分布式计算
本地和分布式计算的主要包括响应时间,内存访问,局部失败和并发。响应时间的区别是最明显的,但是是最次要的。内存访问,局部失败和并发经常被忽视。本地和远程计算之间局部失败和并发的区别是最重要的区别。

4.1 响应时间
响应时间的差距有两种途径解决。一个是提高硬件性能,一个是使用工具来分析对象之间的交互模式。通过分析可以把经常交互的对象部署在相同的地址空间,把相对来说交互较少的对象部署在其他地方。由于从外部看来系统中所有对象(本地,远程)之间的交互使用的是相同的机制,所以这种调整是可行的。当然,首先要保证应用是正确的,其次才是调整性能。
4.2 内存访问
内存访问是更根本的(仍旧明显的)本地与远程计算之间的区别。简单的说,在一个本地地址空间有效的指针在其他地址空间是无效的。系统可以弥补这个区别,但是需要实现完全的透明性。有两个选择:系统负责控制所有的内存访问,或者程序员必须意到本地访问和远程访问的区别。没有中间方案。
如果想要彻底统一编程模型,使得远程访问与本地访问一致,那么底层机制必须完全控制所有的内存访问。一种方法是提供分布式共享内存,这样程序员就可以完全不用关心远程内存访问(或者远程与本地访问的区别)。另一种方法是彻底的使用面向对象的范式,需要完全基于对象构建应用(objects all the way down”),只有对象引用或者值作为方法参数传递,这样可以消除本地和远程计算的边界。底层可以通过编组、解组方法参数和内部地址空间转换来实现该方法。
但是增加一层来将所有指向对象的指针转换为对象引用仅仅允许开发者采用一个统一的对象交互模型。这个统一模型不可能是强制的,除非该语言没有得到相对地址空间指针的能力(译者注:这正是Java的特点呀!看来Java天生适合做分布式计算!)。此外,这种方法放弃了本地和远程计算的完全透明性。

即使语言不允许获得相对地址空间指针(或者总是将这样的指针转换为一个对象引用返回),你仍然需要提供等价的方法为对象之外实体(译者注:什么实体?不是已经objects all the way down,”了么?)的做跨地址空间引用。而且,这样破坏了本地、远程调用完全一直的教义。因为内存访问限制,这两者必须不同。

程序员必须意识到本地访问与远程访问是非常不同的。即使使用接口定义语言为在分布式对象上的操作调用生成一种智能语言映射,程序员还是需要意识到在跨地址空间访问时使用指针带来的错误。
如同响应时间一样,逻辑上是可能掩盖本地和远程内存访问区别的。

4.3 局部失败和并发
尽管可能性很小,但是至少逻辑上是可能在响应时间和内存访问上掩盖本地计算和远程计算的区别的。然而在局部失败和并发情况下,逻辑上也无法掩盖区别。
局部失败是分布式计算的核心问题。在本地和分布式环境中都会有组件出现间歇性失败。在本地计算的情况下,这种失败要么会影响应用中所有的实体,要么被某种中央资源分配器(例如OS)检测到。

然而在分布式计算的情况下,一个组件(机器,网络连接)可以失效而其他组件继续运行。分布式组件的失效是独立的,而且没有公共代理可以决定哪个组件已经失效并通知其他组件关于失效的消息,没有可以帮助判断到底发生了什么错误的全局状态。在分布式系统中,当无法连接到网络一端时,无法区分是一个网络连接失败还是那个连接一端的处理器失败。

在本地计算的情况下,失败就是出现一个异常或者不能完成一个任务。分布式计算中的局部失败可能是因为机器在执行一个对象调用时死机或者网络连接失败,导致目标对象一声不吭的就消失了,不会把控制返回给调用者。分布式的一个核心问题是要确保在发生了这样的失败后整个系统的状态是一致的,在本地计算中没有这个问题。

局部失败对于如何设计接口以及一个接口上的操作的语义有极深的影响。局部失败需要程序处理不确定性。一个局部组件失败,我们可能知道导致失败的系统状态已经失败后的系统状态。在分布式系统中没有这样的确定性。然而,用作通信的接口必须被设计为在发生局部失败时做出一致的反应。

在局部失败面前要做到鲁棒需要一些接口级别的表达式。纯粹提高一个组件的实现是不够的。连接组件的接口必须能够在任何时候明确状态,而且必须有接口在发生了无法确定的失败时允许重构一个合理的状态。

如果一个对象与它的调用者驻留在同一个地址空间就不会发生局部失败。函数调用可能没有正常完成,但是也完成了。

虽然有以上的不同之处,但是依然可以为分布式和局部计算使用单一的对象模型。使用统一对象模型只有两条路可走:

第一个方式是所有对象都按照本地对象来对待,在设计接口时认为所有调用接口的对象,被接口调用的对象都是本地的。这种方式构建的系统在发生局部失败时本质上不确定的,因此是脆弱的。这种模型无法处理局部失败。

第二个方式是把所有的接口设计为远程的。所有的语义和操作都被设计为在全局失败和局部失败时都是可确定的。然而,这样就为那些永远在本地运行的对象引入了不必要的保证和语义。这种模型使得本地计算和分布式计算一样复杂,而不是让分布式计算和本地计算一样简单。所以这种方法违反了使用统一对象模型的目的。

并发
分布式对象本质上必须处理并发方法调用。如果坚持使用统一编程模型,那么并发与局部失败的情况类似。要么所有的对象都必须负担并发语义,要么所有对象必须忽略并发问题。也许有人会说在一个多线程应用中需要处理同样的问题。但是,其间是有微妙的区别的。多线程应用中没有真正的不确定性操作调用的来源。应用程序员可以对调用顺序有完全的控制。分布式系统本质上引入了真正的异步操作调用。而且,非分布式多线程系统是位于一个操作系统层之上的,可以使用os来帮助故障恢复,对象通信以及同步。另一方面,分布式系统没有单一的点来负责资源分配,同步或者故障恢复,所以与多线程应用是大不相同的。

7 严肃的处理本地与远程计算的区别
在响应时间,内存访问,局部失败和并发方面的区别使得合并本地和远程计算模型是既不明智又不可行的。
好点的方式是承认本地和远程计算之间的区别是不可调和的,在设计和实现分布式应用的每个阶段都意识到这些区别。可以通过附加的类定义来表明对象是本地的还是远程的。需要被远程访问的对象意味着某些操作在局部失败的时候还是可靠的。

某个给定的对象可能经常被一些对象访问,而且不允许有不确定性。而另外一些对象很少访问该对象而且允许不确定性。这种情况下该对象应该被分为两个对象(可以共享一个实现),分别为远程访问和本地访问提供合适的接口。

工程师在编写代码时必须知道他们访问的对象是本地还是远程的,以不同方式访问不同对象。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics