Java 版 (精华区)
发信人: rhine (静&&凉爽&&想念你), 信区: Java
标 题: jini核心技术2
发信站: 哈工大紫丁香 (2000年07月17日11:01:23 星期一), 站内信件
第2章 分布式系统
主要内容:
* 分布式系统存在的问题。
* 为何“经典”方法不能满足需要。
* 分布式计算的Java模型。
* 对系统设计者而言动态可下载代码意味着什么。
我们可能都曾见过类似的错误信息:“NFS Server not responding” (NFS服务器无响
应),“unable to locate the server”(不能定位服务器),“please check the
server name and try again”(请检查服务器名称后重试),“no route to host”(
无法路由主机),“host unreachable”(主机不可达)等等,我们的桌面计算机原本
就很脆弱,自从引入了网络,系统就更容易出现各类故障了。
系统的脆弱有很多外观上的原因。显然,在网络系统中有一些新问题会引起系统故障,
一个关键路由器的故障是导致网络应用崩溃的常见原因,但对本地系统就不会有影响。
所谓本地系统,是指在独立的一台机器上运行,只有一个地址空间的应用程序。
当然还有更深层的原因使得建立分布式网络系统比建立独立系统更加困难。事实上,这
些原因也正解释了为什么我们不能抛开网络,使远程系统看起来和本地系统一样:使用
同样的编程模型,代码可自由运行。
本章将讨论网络系统和分布式系统,并把二者区别开来。现在的很多软件,尤其是在We
b出现之后,都是基于网络的,而Jini代表了一种更复杂的模式:一种真正动态的分布式
系统。下面看一下两类软件的不同之处,分析一下为什么分布式系统编程的基本架构还
有些棘手的问题需要解决。建立在Java基础上的Jini为分布式系统的问题提供了一些很
好的思路,我们将分析一下这种基于Java的方案与以前的方法有何不同。
2.1 网络中的焦点
为什么难以创建健壮的网络软件呢?我们先看一下原来是用了哪些方法来解决网络编程
中的问题,而这些方法又存在什么缺陷。或许能找到些答案。
2.1.1 传统网络系统
网络系统编程的历史很多都是试图隐藏网络的存在。研究一下“经典”网络系统的架构
,可以看到两个共同点,第一,它们都提供把数据转移到计算之处的方式;第二,它们
都对处理数据的方式进行了明确的规定,这些方式来自一个可选择的约束集合。
因为不可能使完成某个指定操作所需的代码在网络中随处可用,所以把数据转移到计算
发生的位置是必需的,实际上因为程序需要在不同的机器上编译和安装,程序可到处运
行的设想很不容易实现。从这个角度看,转移数据要比转移代码容易得多,但转移数据
也有很多问题,如不同的字节顺序、不同的浮点格式、不同的字长等等。XDR(Externa
l Data Representation,外部数据表示,Sun公司为支持可移植性使用的一种数据打包
协议,从80年代中期开始使用)等数据表示工具通过隐藏各机器间不同的数据格式,使
得各种CPU都可以识别自己所接收的数据,从而“隐藏”了网络的存在。这些工具通过标
准化一种可移植的特定线路格式,使数据独立于各种机器自己的字节顺序、字长等,从
而支持数据在网络中传输,无错误到达到需要的地方。
当数据到达目的地后,常用远程过程调用(RPC)或远程对象系统的方法进行处理。远程
过程调用系统试图使对另一台机器上的过程进行调用看起来像是对本地同一地址空间中
过程进行调用;远程对象系统如CORBA和DCOM把编程的级别从过程调用抬高到对象的调用
,但本质上是模仿本地调用的方式。在过去20年中,远程对象系统与远程过程调用技术
没有本质的区别。
当然这些隐藏网络的努力是基于一个重要的原因:程序员最初只理解基本的本地编程技
术,他们知道如何构造可靠的独立系统,而如果增加一些引入了网络的特殊编程模型,
他们就需要花费更多的时间和精力学习新的模型。
因此这些系统都尽量使网络“透明”,也就是说,它们要使程序员感觉不到网络的存在
。
2.1.2 网络并不透明
不幸的是,这种简化有些过份,这些系统基于一种隐含的假设—在两个软件程序之间引
入网络,并不会影响它们的正确,只会影响效率。事实上,在传统的网络系统中确实需
要隐藏特定对象的位置,就像在面向对象系统中把过程的实现隐藏而只提供接口;程序
在网络出现故障或性能过低的情况下如何工作,应当是其实现的另一方面,而不是接口
或者它与系统其他部分交互的功能。
这样说来,建立可靠的分布式系统最困难的问题并不是使用远程过程调用的系统所关注
的:把数据转化为可移植的格式、调用网络上远程的过程等,相反,建立可靠的分布式
系统最困难的是那些程序员不能忽略的网络事实—访问远程资源用的时间比访问本地相
同的资源大许多,网络会出现独立系统不存在的崩溃方式,网络系统中的一部分出现故
障时会导致系统处于不一致的状态,这些问题需要认真处理。
计算机专家Peter Deutsch很多年前就认识到了这一问题,并列出了关于分布式计算的七
个错误认识,我们在工具条中给出了他的观点,在这里不再详细讨论。即使使用一些经
典的系统,创建可靠的、可伸缩的、健壮的分布式系统仍然十分困难,这也是我们经常
在网络上看到如此多错误信息的原因。经典的网络系统“欺骗”了无知的人们,使他们
相信可以忽略某些实际并不能隐藏的问题,从而使分布式系统编程中潜伏了很多这样的
问题。下面我们逐步展开讨论。
工具条:分布式计算的七个错误认识
计算机专家Peter Deutsch早就对被他称为“分布式计算的七个错误”的相关问题进行了
论述,这里列出这些错误认识以及他的一些分析。
“基本上每个人在第一次创建分布式应用时,都会基于下列七种假设,但在长期工作中
,这些假设被证明是错误的,会给人们带来很大的麻烦,甚至痛苦的经历”。
* 网络是可靠的。
* 延迟(latency)是零。
* 带宽(Bandwith)是无限的。
* 网络是安全的。
* 拓扑(Topology)不变。
* 只有一个管理员。
* 传输无代价。
Jini中的大部分设计工作是在明确认清了这些错误的基础上进行的,对它们不再是视而
不见。
1. 性能和延迟
显然,通过网络访问资源,如一个文件或一个对象,要比本地的访问慢。从访问时间上
看,它们之间的差别常是几个数量级,可以说它们之间不再仅是量的差别,而是质的差
别。
或许读者会说:“有这么严重吗?访问Cache要比访问主存快几个数量级,而访问主存要
比访问磁盘快几个数量级,这难道不能看作是性能等级的另一个层次?”这里问题的关
键在于,网络系统中的性能差别太大,以致于系统常难以区别究竟是连接失败还是过慢
。尽管主存比磁盘的速度快,但二者的性能参数都能保持在一定的范围内,可是使用过
Web浏览器的人都有过体会,网络的性能差别特别大,即使在一小段时间内也是如此。本
地系统中的访问时间不会像远程访问时间那样影响程序。
可是RPC和CORBA这样的系统并没有考虑这些性能问题。这里我并不是说要它们把网络性
能提高成百上千倍以减弱延迟上的差别,实际上它们也做不到;但是RPC和CORBA根本不
把性能和延迟当作它们编程模型的一部分,就带来了一些麻烦,它们只是简单地完全忽
略掉这个问题,把它当作是程序员毫无疑问要认识到并进行处理的部分。
如果在开发时忽略掉这些差别(因为远程访问和本地访问方式十分相近),就意味着在
分布式系统中不仅会出现性能极差的设计,而且可能出现不能够处理网络中性能差异的
设计。如果一个系统在设计对象的接口之前未考虑本地或远程的“实现细节”,那么这
个系统的通信模型就会特别慢,而且不够健壮。
我们必须要在考虑对象间通信接口的设计时就考虑这类“远程”访问结构的细节,到设
计应用时再考虑则有些太晚了。
2. 新的失败形式
网络系统存在一些独立系统中没有的失败形式:路由器可能失败,用户会有意或无意地
把网线从墙中扯出,运行域名服务器或数据库服务器的关键机器可能崩溃等等。如果把
在独立环境中编写的代码放在网络环境中运行,它们会对这些新失败形式产生的后果无
所适从。
举一个简单的例子:一个从磁盘上读取文件的函数。这个程序段在一个大的应用中重复
运行,把文件以电子表格的形式显示出来,并可以按原来的设定正确地写入。这个程序
可以智能地检测并处理各类错误,包括文件系统溢出、许可权违例等等。
现在考虑运行这样一个应用程序,它用来处理网络文件服务器磁盘上的文件。原来它可
以智能地处理本地的各类故障,现在却必须面对各种网络故障。文件服务器主机可能关
闭,或是不能访问到。在有些情况下,应用程序可能会像对待本地错误时一样以“正常
的”方式退出,例如它可能会像对待许可权冲突一样对待服务器关闭的情形,但是它也
极有可能会以不可预料的方式崩溃:这个程序当然不能智能地处理网络错误,如在服务
器不可达时重试,或是在主文件服务器故障时提示使用后备文件服务器。
或许仍然有疑问:“这有什么呢?我们可以在执行完某个操作后检测一下看看是否有失
败发生,包括网络。这就像在本地系统中,做完磁盘写操作后检查一下数据是否确实已
正确地写入一样”。这种方式并不可行,其原因是,在分布式系统中,通常难以甚至不
可能检测错误是否存在。在本地系统中,可以试着向磁盘写入一些数据然后检查一下数
据是否被正确写入,但是在分布式系统中,通常没有中心机构(就像本地的操作系统)
来判断是成功还是失败,即使TCP/IP这样的“可靠”协议也不能解决问题。不管TCP/IP
多么可靠,它不能区别一个应用程序到底是崩溃了还是因为太慢没有反应,没有集中的
机制作出判断。
如果系统在其接口中不考虑在网络环境中可能出现的失败,那么这种“隐藏网络”的编
程模型实际上是默许了程序员可以忽略网络故障;而如果能在接口中体现出远程访问对
象时可能存在的问题,我们就可以强制程序员—通过编译器—来注意这些问题。
3. 并行性和一致性
在本地和远程计算各方面的差别中,性能和延迟的差异以及故障形式的差异是十分明显
的,但分布式计算中特有的并行性和一致性问题带来的影响要更为严重些。在本地系统
中,如果一个软件实体出现故障,这种故障或者是全局性的,或者无甚关系。如果整个
机器停机,那么只是该机器上的程序停止工作,与网络上其他机器没有关系,如果协同
操作的一组应用中的一个出现故障,比如剪贴操作中的一两个应用程序出错,中心控制
系统如操作系统就可以检测到这样的错误并通知其他相关部分。
在分布式计算的情况下就不再是这种情形。在分布式系统中经常是,某一部分出现故障
而其他部分继续运行,感觉不到这一部分的失败。这种情况被称为部分失败(partial
failure),是分布式计算中各种问题的主要根源,潜伏性强。全局失败(total failu
re)和本地系统中的情形差不多,因为系统终究是以一个确定的状态结束,所以容易处
理些。
下面来看一个白板应用程序的例子。这个程序允许网络上的一组参加者作出标记,共享
一个公共的画图区。在一个用户作出标记后,这个更新情况应该迅速地传送出去或者通
过多次单点传送,即向每个参与者重复发送相同的信息,或者是通过组播,即一次把信
息传送到许多在网络上监听的特定地址。无论在哪种情况下,接收方的计算机都可能在
更新的过程中出现故障或者是不可到达。
那么会出现什么情况呢?如果发送者不能检测到这类故障(可能是因为写例程原来是用
于独立系统,或者是因为操作系统不能判断消息是否已经发出),那么就没有办法知道
有一个参与者的信息与其他人的内容不同步。如果一段时间后网络又恢复正常,未接收
到更新的程序继续运行,根本不知道自己错过了一次更新。
你可能会想,只要保证发送者可以知道一个消息没有被正确接收就可以了,即使操作系
统不支持,应用程序开发者也可以用附加序列号以及确认报文的方式判断是否接收到了
更新内容(实际上可用的协议要比这复杂,因为发出确认消息的一方仍无法知道自己的
确认消息是否被正确接收)。即使是如此,最初的发送者在检测到更新未被发送给接收
方时又能做些什么?一种强硬的做法就是判定此接收方已经崩溃而且不能恢复,使这个
参与者退出会话;而一种比较委婉的做法就是发送方保留更新的内容,期待着这个接收
者会重新连到网络中,然后重新发送更新内容。不过这些更新内容要保存多久呢,接收
恢复前的所有内容吗?我们当然不希望一直为某个情况不明的接收方保留所有的消息,
因为这样会使发送方为一个非本地的错误耗去很多资源。
这个共享白板应用程序的例子很小,但它说明了把分布式开发的复杂问题隐藏起来会带
来很多问题。就像性能及失败形式所带来的影响一样,部分失败以及维护各部分间的一
致性也带来很多不利因素,在进行分布式编程时必须慎重对待。过去的方案只是试图隐
去本地和远程计算的差别,对这些问题不加考虑。当然事实上它们也不可能解决这些问
题:在出现部分失败或一致性冲突时,应该是每个应用程序开发者决定如何处理,而不
应该交给操作系统或工具套件,对所有应用程序使用一种通用的方式。每个应用程序都
有不同的需要,因此需要以不同的方式处理部分失败,一个必须要求完全一致性的应用
程序在检测到不能保证恢复的部分失败时,只要简单地关闭应用;而另外一些应用程序
也许能够容忍部分失败的存在,也可能只允许在一小段时间内存在。总之,系统在处理
部分失败时一定要依赖于每个应用程序的实现方式,程序员必须在自己的系统中认清并
处理这些故障。
因此,分布式系统的开发者必须清楚网络编程和本地编程间的差别,慎重选择最适合自
己应用需求的方案,或决定作出某些折衷。尽管分布式系统的研究已经开展了30年,创
建分布式的软件仍然十分困难,原因在于过去的工作集中于数据的可移植性以及如何把
数据转移到计算的场所,而这些并非最困难的问题。
2.2 新的分布式计算模型
尽管不能解决所有问题,Java还是在这些使分布式系统编程变得困难的问题上取得了很
大的进步。首先,Java很好地解决了经典系统所关注的大部分问题,而还有一些原来的
问题在Java中根本就不必考虑,因为Java是一种面向语言的系统,整个Java平台,包括
JVM、类库、字节码验证器、安全体系结构等,都以Java字节码解释所有的对象,这种方
式可以从很多方面简化分布式系统的创建工作。在CORBA及DCOM这样的系统中,分布式特
性是基于一种或多种语言的固定附件,而Java的方案是假定这种特别的语言无处不在,
数据格式和代码格式都完全统一。
这样数据转移就不再有什么问题。基本类型如整型、浮点数、字符串、数组,甚至还有
对象的大小和格式,都严格按Java规范进行定义,不需要特殊的机制在一台机器的数据
格式与另一台机器的数据格式之间进行转换。C语言就完全不同,其语言本身未对本数据
类型的大小和格式作明确规定,我们只能依靠语言之外的工具来描述可移植的数据。
除了定义基本类型外,Java还定义了完整对象的格式,包括子类化、方法保护属性、类
型标记等的细节。定义如此详尽的数据,是为适应XDR等把数据统一为可移植类型的机制
。
另外,在Java出现前关于分布式计算的一个最主要的假设,即把数据移向代码(相反的
操作不可行),也已失去意义。使用Java,代码可以方便地在机器间转移,即使是操作
系统或CPU比较特殊的机器也无所谓。当然移动数据也没有问题。对于可执行代码,Jav
a的字节码格式是用于交换的统一格式。另外,Java的安全机制可以保证转移到本地机器
上的代码可以在高度的安全性下执行,其他方法做不到这点。或许操作系统可以通过共
享对象文件或动态链接库的方式支持动态链接代码段,如C代码,但是Java的安全动态加
载工具可以更好地支持类似功能。
例如,Java通过安全策略可支持对系统资源细粒度的访问控制,代码可使用数字位标注
,为类的来源提供加密的安全保证。更重要的是,Java的强类型的安全性以及字节码验
证器的能力,可以保证不良的代码不会被运行。也就是说,在运行远程下载的代码时,
可保证引起整个系统崩溃的机率非常小。例如,在C语言中,如果动态加载的代码段中有
空指针,或是提供了越界的数组下标,或是破坏了堆,就可能导致另一个长时间运行的
服务程序出错。在Java中,不存在对未使用数据的引用,而且不可能把新的引用强加到
未被明确给以访问许可的对象上。另外,在C中一个函数可能会返回些任意的值(也不是
期望的)来标识错误情况,而在Java中,异常的情况必须作为方法声明一部分,强制那
些调用此代码的程序处理。这种安全属性使Java可限制动态加载的代码破坏整个系统。
可以在机器之间转移代码是一个伟大的变化,这样,服务器就可以动态更新,可以在被
授权的客户端进行维护。Agent代码可以在网络中移动,靠近所需的代码以提高性能。建
立在严格定义的静态接口上的系统,可以通过提供这些接口新实现的代码来动态升级。
如此看来,在那些过去的系统所关注的许多问题上,Java确实提供了一些新的方案,而
且还使过去的系统中关心的另一些问题不再需要考虑。但对于我们在本章开始部分所讨
论的分布式计算中那些真正困难的问题,Java又怎么样呢?Java是如何区别对待有本质
区别的本地和远程对象的呢?
在理解Java如何解决这些难题之前,下面先来看看用于Java的强类型分布式计算模型。
我们将会了解到,Java并没有提供特别的方法解决上述问题,Java的分布式计算模型明
确认可本地和远程计算过程的差异,并提供一个支持编程人员解决这些问题的工具集。
2.2.1 需要强类型
前面一部分主要是讨论Java支持把代码发送到需要运行的场所的能力,但这确实是我们
需要的吗?一个可执行代码满天飞的环境就真的易于维护和管理吗?作为一种基于语言
的方法,Java可以充分利用本地系统中强类型、真正的面向对象、以及多态性带来的好
处,并把这些优点扩展到Internet上。
在远程方法调用(RMI)时,Java语言的类型定义系统用来描述远程通信的接口。这看起
来不太重要,事实并非如此—声明Java类或接口时所创建的本地类型以及Java类库中的
类型,与用于远程通信的类型是采用完全相同的机制定义的,也就是说,用于和远程服
务器进行通信的协议,其实就是Java接口,可以通过一个类来实现。下面是一个接口定
义的例子,它出现在使用Java RMI的程序中:
public interface RemoteServer extends Remote{
public int getLength (String s) throws RemoteException;
}
这个例子实际上是在定义两个远程进程间的远程“协议”,它定义了两个进程间相互作
用的术语,包括发送什么、可做什么操作、可得到什么结果。当然这个协议要在运行其
他低层协议如TCP/IP的基础上实现(RMI将处理所有建立、撤销套接字等便利的操作),
而这里的定义给两个进程如何在应用级进行通信提供了规约。总体上看,这个例子说客
户端可以发送一个传送字符串的消息到远程的服务器,服务器返回一个整数作为对消息
的应答。可是这个例子还在Java语言中定义了一个一级接口,这才是RMI的关键所在。这
个一级接口可以由类来实现,通过多接口继承、作为参数传送、从方法中返回、自省(
introspect),由其他多个接口构成。
你或许会问:“这有什么呢?”在其他的系统如CORBA的接口定义语言(IDL)中,用于
与服务通信的远程协议也是本地语言的一种类型。不同的是, RMI完全使用类型来定义
协议,类型就代表了协议定义的“真理”,可以在Java语言中随意修改类型以扩展或改
变协议,而像IDL那样的系统是使用协议的一些外部表示来描述协议的“真理”(经常是
用中性语言的形式来定义协议)。协议以Java类型来表示是在既成事实之后,Java类型
只不过是协议定义的映像,不是协议定义本身。通过子类化、构造及映像的方法操作出
自IDL规范的Java类型,丝毫不会改变IDL中用于通信的协议。
这些不同之处看起来比较微妙,而且有些学术性的味道,但它的重要性不容低估。通过
在新型面向对象语言中以一级对象的方式定义协议,可以极大地发挥面向对象的优势,
使其在分布式计算环境中仍能像在单地址空间中一样正常工作。
多态性也依然存在,例如我可以定义一个远程接口是其他远程接口的子接口。多态性的
规则依然适用,我们可以传送一个更通用的接口,在需要的时候将其投射到更专用的接
口上,然后在运行时使用instanceof来检查一下实际是使用了哪一个接口。就像接口可
以有多种实现方式一样,可以为一个特殊的远程接口实现多个Java类,编译器将强制开
发者以正确的方式实现所需的方法。就像可以编写实现多个本地接口的类一样,我们也
可以编写实现多个远程接口的类。
2.2.2 远程多态性的例子
比在远程接口中支持多态性更进一步的是,对于作为参数传送给远程方法或作为远程方
法返回结果的对象类型,RMI也支持完全的多态性。我们来看一下这样一段例子代码:
public interface MatrixSolver extends Remote{
public Matrix crossProduct (Matrix m1, Matrix m2)
throws RemoteException;
}
这段代码定义了一个用于矩阵计算的远程接口(假设实现这个接口的远程对象运行在高
端处理器上,它处理矩阵计算十分快,接收参数和发送结果的时间与可节省的时间相比
小得多),这个接口接收两个Matrix类型实例的参数,计算它们的叉积,返回一个Matr
ix类型的结果。
显然设计者希望调用者接收和发送Matrix类型的对象,并可以理解这个类的定义。可是
假如我是一个信号处理工程师,我自己开发了处理矩阵的特殊类,这个类称为SparseMa
trix,是标准的Matrix类的子类,它的数据项更少,但效率更高,因此我在自己程序中
全部使用它。不过我希望能利用远程的矩阵计算对象,因为它们运行在高性能的机器上
,不幸的是这个程序由其他的小组开发,只认识基本的Matrix类,不能识别我的Sparse
Matrix类。
显然,在本地系统情形下,Java类型的多态性可以帮助我解决问题:我仍然把Sparse M
atrix实例传递给只识别普通Matrix类的代码,crossProduct ( )函数可以正常工作,因
为它只使用我的类中的Matrix部分。更好的crossProduct ( )函数甚至会调用clone (
)例程来复制它的一个输入参数以创建一个相同类型的对象用于填写返回的结果,那样的
话返回的结果就和输入是相同的类型了。
但是在远程系统的情况下如何呢?如果使用RMI,就会和本地系统完全一样,即使远程的
矩阵计算服务器不知道SparseMatrix类,而且远程机器上也没有这个类的代码,RMI也会
把一个SparseMatrix实例和数据一起打包发送到远程。重要的是,RMI会在需要时把这个
类的代码传送出去,这样可以确保SparseMatrix作为Matrix的一个子类实现所有需要的
方法,从而可以在需要使用Matrix类时安全可靠地使用SparseMatrix类。
这是一个十分实用的例子。动态可移动的代码不仅在新兴的应用如主动Agent中有用,而
且对于把面向对象编程扩展到网络系统具有重要的作用。
2.2.3 远程特性是接口的一部分而与实现无关
在上面一节那个短小的例子中,还可以注意到RMI带来的另外一个好处—一个对象的“远
程特性”,即可在其他地址空间使用的特点,只是对象接口的一部分。这是与主流不同
的另一个巧妙变化,它强迫开发者从一开始就考虑程序的远程特性,而不是稍后再添加
,这种选择使得程序员必须事先考虑好他们的应用要采用哪种通信模式。一个方法的远
程特性必须被明确声明,而不能作为对象实现的一部分被隐藏起来。而且就像Java类型
系统中的其他类型一样,一个对象的远程特性可在编译时被强制转换类型,然后在运行
时自省。
RMI要求所有定义了远程通信协议的接口都要扩展接口java.rmi.Remote,这个接口对所
有可远程使用的对象是公共的,因此在编译或运行时可以很容易地判断一个对象是否支
持远程使用。这些对象和简单的本地对象有着不同的语义,比如它们对相等的理解就不
相同。在远程对象中,两个对象相等是基于两个对象是否指向了某个服务器上的同一个
远程对象,而不是指这两个对象是否为同一个。也就是说,如果你引用了一个远程对象
,而事实上指向的是那个远程对象在本地的代理,称为“存根”,那么在比较两个引用
时,你并不关心它们是否指向了同一个本地的“存根”,你只是关心这些“存根”是否
指向了远程机器上的同一个远程对象。因此在远程系统中相等的概念被扩展了。
或许更重要的是,每一个可远程调用的远程对象中的方法都声明产生异常java.rmi.Rem
- oteException。这个异常和它的子类一起,定义了在网络环境中可能出现的失败形式
。通过在方法的标记中明确声明这些失败情况,RMI强制那些使用远程对象的程序员要提
供处理这些异常情况的代码以应付失败的发生。因为Java声明异常为方法标记中的一部
分,因此调用远程代码的程序要么是自己处理各类远程异常,要么是把这些异常向上传
播到可处理的程序,编译器不允许程序员忽略掉这些情形。
远程方法调用RMI当然不是万能的,因此它不能解决分布式系统中所有的问题,或是使得
建立分布式系统像建立本地系统一样简单,但它的思路是正确的。RMI使用Java类型系统
,但对本地和远程对象的语义进行了区分。它强制开发人员处理分布式环境可能出现的
故障,要求他们把分布式明确地声明而不是在实现中隐藏。为利用Java语言的强大功能
,RMI使用了强类型,并将其扩展到网络领域。RMI使得数据可移植性不再成为一个困难
问题,而且通过支持安全代码的概念,RMI把面向对象编程的优点引用到了网络系统,支
持灵活可伸缩的远程代码。RMI还提供了一个功能强大的工具包,用于帮助开发者解决在
创建分布式应用时可能遇到的问题。
当然,Java因可移动代码而著名,但它不仅仅是用于RMI环境中,下面这个工具条用于比
较可移动代码在RMI中的应用和在其他情况下的应用。
工具条:其他上下文中的可移动代码
在前面几节举了几个例子说明可移动代码的用处,这些例子与可移动代码在其他上下文
中的应用相比较如何呢,如Applet?下面总结了一些使用可移动代码的上下文,并指出
了可移动代码在其中的作用。
* Applet:是Java最著名的一部分内容。使用Applet,小应用程序可以自动安装到需要
它的地方,并在用户不需要时自动卸载。在此上下文中使用可移动代码,就省去了很多
安装传统的应用程序时遇到的复杂问题。有了Applet,安装应用程序可以在浏览Web页时
进行。
* Agent:在Agent模型中,自治的小段代码在Internet中游走,寻找所需的数据。例如
,一个“旅行Agent”可能会在网络上寻找飞往亚特兰大的最佳方式。在此上下文中,可
移动代码有两个用处,性能和自治。Agent可以提高性能,因为程序靠近了数据(从网络
的角度看);自治性也是Agent必须的,因为启动Agent的用户可能会退出系统或关掉机
器。离开了最初的机器后,尽管与原来机器失去了联系,Agent也应该能保持继续运行。
* RMI:Java的远程方法调用事实上是一个用于创建各种分布式系统的基本结构,而不是
一个单独的分布式系统,因此RMI可被用于自动安装应用程序(和Applet很像),或是用
于构建基于Agent的系统。不过可移动代码在RMI中最主要的作用是把面向对象的优点扩
展到了网络系统中,而且允许通过重新实现远程对象以及参数和返回值的类型,提供系
统的可扩展性。
* Jini:尽管这里还没有正式讨论Jini,但我们知道Jini是利用RMI以及可移动代码,使
网络上的设备和服务易于维护、易于扩展、易于管理。因为它建立在RMI之上,充分利用
了Java语言的优势,所以使用Jini编程可体会到可移动代码的所有好处。
Java是第一个广泛应用的在很多领域、支持可移动代码的、可行的商业化可视系统。
可移动代码、可移动数据以及强类型(健壮性需要)的概念使得建立一个真正的动态分
布式系统成为可能,这样的系统由许多构件组成,它们一起工作来到达目标。这和独立
的网络系统不同,独立的网络系统只是一个单独的部分,它们只是以固定的静态方式彼
此连接。
下面是动态分布式系统的一些特性:
* 动态分布式系统可以潜在地扩展到很多的机器。
* 在扩展时,它们的代码要在所有机器上可见;为了出现在各种机器上,这些代码必须
到处可用;为做到到处可用,代码必须易于安装。
* 在配置之后,动态分布式系统应该可以长时间运行,甚至是几个月或几年的时间。因
此它们必须是健壮的、可靠的,并且能在出现错误时“自修复”。
* 任何长时间使用的系统都应该可扩展,以适应新的网络拓扑、新设备的加入以及接口
和实现代码的更新。因此,动态分布式系统应该适应性强,易于升级。
所有这些属性都归结于一组软件,在软件的支持下,系统由多个离散的单元组成,系统
中每个单元对其他单元来说都具有一定的功能,所有成员一起协同工作,组成一个有意
义的具有自修复能力的系统,这迥异于单个成员的累加。
在第3章,将开始接触Jini—一种专门支持创建动态分布式系统的软件系统。
2.3 参考读物
本章内容的形式主要有两个来源。一是Peter Deutsch给我的一个报告,当时我刚在Sun
实验室做过一个发言,其中提到“网络透明性”是我从事的一个系统的主要优点,Pete
r立即以他特有的方式向我证明,我的观点事实上是错误的。
另外是Jim Waldo、Geoff Wyant、Ann Wollrath和Sam Kendall合写的一份出色的报告,
也是来自于Sun实验室。这些人名我们已经很熟悉,因为正是他们提出了开发RMI和Jini
的思想,这份报告要早于Jini几年,但其观点现在和当初一样正确,阅读这份报告可使
你认识到Jini的设计者是如何考虑那些分布式计算中的重大问题的:
http://www.sunlabs.com/technical-reports/1994/abstract-29.html
--
海纳百川,
有容乃大,
壁立千尺,
无欲则刚。
※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: dip.hit.edu.cn]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:206.744毫秒