最近在学习分布式应用程序开发,以前也有接触,但说实话,了解地不够深入.最近因项目较少,时间比较宽裕,因此打算好好地深入学习研究一下.个人认为,学习任何一门编程语言,最好的学习方式就是先从官方提供的文档入手,因为这是知识的源头.
在进入正题之前,不得不说一下,上网的时候经常看到这些言论,对于某一本书或某一篇文章的翻译,看客们都是一片谩骂之声.个人觉得这实在不应该,首先翻译者有勇气去翻译,并拿出来分享,我就觉得这是一种不错的精神体现.知识在于分享,只有这样,才会学到更多的东西.
以下是对J2SE1.6 RMI官方指南的翻译,如果有不够准确的地方,请各位朋友批评指正,你的批评指正是我不断前进的动力.
原文地址如下:
http://docs.oracle.com/javase/tutorial/rmi/overview
An Overview of RMI Applications
RMI applications often comprise two separate programs, a server and a client. A typical server program creates some remote objects, makes references to these objects accessible, and waits for clients to invoke methods on these objects. A typical client program obtains a remote reference to one or more remote objects on a server and then invokes methods on them. RMI provides the mechanism by which the server and the client communicate and pass information back and forth. Such an application is sometimes referred to as a distributed object application.
RMI应用程序概述
RMI应用程序通常由两个单独的程序组成,即服务端程序和客户端程序.一个典型的服务端程序通常用于创建远程对象,并使其引用可以被客户端程序访问以及等待客户端调用这些对象的方法.
一个典型的客户端程序用于获取服务器上一个或多个远程对象的引用,然后调用这些远程对象的方法.RMI提供了客户端和服务端程序来回通信和传送信息的机制.像这样的程序通常称为分布式对象应用程序.
Distributed object applications need to do the following:
?Locate remote objects. Applications can use various mechanisms to obtain references to remote objects. For example, an application can register its remote objects with RMI's simple naming facility, the RMI registry. Alternatively, an application can pass and return remote object references as part of other remote invocations.
?Communicate with remote objects. Details of communication between remote objects are handled by RMI. To the programmer, remote communication looks similar to regular Java method invocations.
?Load class definitions for objects that are passed around. Because RMI enables objects to be passed back and forth, it provides mechanisms for loading an object's class definitions as well as for transmitting an object's data.
分布式对象应用程序需要按以下步骤进行:
?定位远程对象.应用程序可以使用多种机制来获取远程对象的引用.比如,应用程序可以将其远程对象通过RMI注册机进行绑定.作为一种选择,应用程序可以通过作为其他远程调用的一部分传递或返回远程对象的引用
?与远程对象通信.远程对象之间的通信细节是由RMI来处理的.对于程序员来说,远程通信看起来是与正常的Java方法调用是相似的.
?加载传递对象的类定义.因为RMI允许对象来回的传递,所以它提供了一些机制来加载对象的类定义,这些机制和传送对象数据的机制是一样的.
The following illustration depicts an RMI distributed application that uses the RMI registry to obtain a reference to a remote object. The server calls the registry to associate (or bind) a name with a remote object. The client looks up the remote object by its name in the server's registry and then invokes a method on it. The illustration also shows that the RMI system uses an existing web server to load class definitions, from server to client and from client to server, for objects when needed.
下面的图描述了一个分布式应用程序使用RMI registry来获取一个远程对象的引用.服务端程序调用registry将名字与远程对象进行绑定.客户端程序通过名字在服务端的registry查找远程对象,然后调用其上的方法.这张图同时也展示了RMI系统在需要对象的时候,无论是从服务端到客户端或是从客户端到服务端,都会使用已存在的web服务器来加载类的定义.
Advantages of Dynamic Code Loading
One of the central and unique features of RMI is its ability to download the definition of an object's class if the class is not defined in the receiver's Java virtual machine.
All of the types and behavior of an object, previously available only in a single Java virtual machine, can be transmitted to another, possibly remote, Java virtual machine.
RMI passes objects by their actual classes, so the behavior of the objects is not changed when they are sent to another Java virtual machine.
This capability enables new types and behaviors to be introduced into a remote Java virtual machine, thus dynamically extending the behavior of an application.
The compute engine example in this trail uses this capability to introduce new behavior to a distributed program.
动态代码加载的优势
一个RMI主要的且独一无二的特性就是当接收者的Java虚拟机中没有对象的类定义时它可以自己下载对象的类定义.位于一个虚拟机中的对象,其所有数据类型和行为都可以传送到另一个Java虚拟机.
RMI是按对象的实际类来传送的,所以当它们被发送到另一个虚拟机时,其对象的行为是不会改变的.通过这种能力,使得新的类型和行为可以被引入到远端Java虚拟机,因此也就动态地扩展了应用程序的行为.
compute engine示例就使用这种能力来为分布式程序引入了新的行为.
Remote Interfaces, Objects, and Methods
Like any other Java application, a distributed application built by using Java RMI is made up of interfaces and classes.
The interfaces declare methods. The classes implement the methods declared in the interfaces and, perhaps, declare additional methods as well.
In a distributed application, some implementations might reside in some Java virtual machines but not others. Objects with methods that can be invoked across Java virtual machines are called remote objects.
An object becomes remote by implementing a remote interface, which has the following characteristics:
?A remote interface extends the interface java.rmi.Remote.
?Each method of the interface declares java.rmi.RemoteException in its throws clause, in addition to any application-specific exceptions.
RMI treats a remote object differently from a non-remote object when the object is passed from one Java virtual machine to another Java virtual machine.
Rather than making a copy of the implementation object in the receiving Java virtual machine, RMI passes a remote stub for a remote object.
The stub acts as the local representative, or proxy, for the remote object and basically is, to the client, the remote reference.
The client invokes a method on the local stub, which is responsible for carrying out the method invocation on the remote object.
A stub for a remote object implements the same set of remote interfaces that the remote object implements.
This property enables a stub to be cast to any of the interfaces that the remote object implements.
However, only those methods defined in a remote interface are available to be called from the receiving Java virtual machine.
远程接口,对象,方法
像任何其他Java应用程序一样,通过使用Java RMI构建的分布式应用程序是由接口和类组成的.接口用于声明方法.类用于实现接口中定义的方法,也有可能定义一些额外的方法.
在一个分布式应用程序中,一些实现可能存在于一些Java虚拟机中.远程对象是指那些其方法能被Java虚拟机调用的对象.
一个对象可以通过实现remote接口成为远程对象,它一般有如下特征:
?一个远程接口继承自java.rmi.Remote接口
?接口中的每一个方法都要在throws子句中声明抛出java.rmi.RemoteException,也可以声明与应用相关的异常.
在将对象从一个Java虚拟机传送到另一个Java虚拟机时,RMI在对待远程对象与非远程对象是不同的.它不是在接收的Java虚拟机中拷贝实现对象,而是传递一个远程对象的远程存根(stub).
存根为远程对象作为本地的代理.客户端在本地stub上调用方法,此stub负责实现远程对象的方法调用.
为远程对象服务的stub实现了与远程对象同样的远程接口.此属性允许一个stub转换为任何一个远程对象实现的接口.但是,只有那些定义在远程接口中的方法能被接收的Java虚拟机调用.
Creating Distributed Applications by Using RMI
Using RMI to develop a distributed application involves these general steps:
1.Designing and implementing the components of your distributed application.
2.Compiling sources.
3.Making classes network accessible.
4.Starting the application.
使用RMI来创建分布式应用程序
使用RMI来开发一个分布式应用程序包括如下一般步骤:
1.设计和实现你的分布式应用程序的组件
2.编译源代码
3.使得classes能够通过网络访问
4.开始运行应用程序
Designing and Implementing the Application Components
First, determine your application architecture, including which components are local objects and which components are remotely accessible. This step includes:
?Defining the remote interfaces. A remote interface specifies the methods that can be invoked remotely by a client. Clients program to remote interfaces, not to the implementation classes of those interfaces.
The design of such interfaces includes the determination of the types of objects that will be used as the parameters and return values for these methods. If any of these interfaces or classes do not yet exist, you need to define them as well.
?Implementing the remote objects. Remote objects must implement one or more remote interfaces. The remote object class may include implementations of other interfaces and methods that are available only locally.
If any local classes are to be used for parameters or return values of any of these methods, they must be implemented as well.
?Implementing the clients. Clients that use remote objects can be implemented at any time after the remote interfaces are defined, including after the remote objects have been deployed.
设计和实现应用程序的组件
首先,决定你的应用程序的架构,包括哪些组件是本地对象以及哪些组件可以被远程访问.这些步骤包括:
?定义远程接口.一个远程接口定义了能被客户端远程调用的方法.对于使用远程接口的客户端程序,它们不是这些远程接口的实现类.接口的设计还包括对象数据类型的决定,这些数据类型将被用作参数和方法返回值的数据类型.如果接口和类都还不存在的话,你必须先好好地定义他们.
?实现远程对象.远程对象必须一个或多个远程接口.远程对象的类可以包括其它接口和方法的实现.如果任何一个本地类被用作这些方法中的任何一个的参数和方法返回值,它们必须被好好实现.
?实现客户端.使用远程对象的客户端可以在远程接口定义之后的任何时候实现,包括在远程对象被部署之后.
Compiling Sources
As with any Java program, you use the javac compiler to compile the source files. The source files contain the declarations of the remote interfaces, their implementations, any other server classes, and the client classes.
Note: With versions prior to Java Platform, Standard Edition 5.0, an additional step was required to build stub classes, by using the rmic compiler. However, this step is no longer necessary.
编译源码
同任何一个Java程序一样,你可以使用javac编译器来编译源文件.此源文件包含远程接口的声明,远程接口的实现,任何其它的服务端类,以及客户端类.
注意:在Java平台的先前版本以及标准版本5.0中,还需要一个附加的步骤,即通过rmic编译器构建stub类.然而此步骤现在已经没有必要了(译者注:从JDK1.6开始,此步骤可省略).
Making Classes Network Accessible
In this step, you make certain class definitions network accessible, such as the definitions for the remote interfaces and their associated types, and the definitions for classes that need to be downloaded to the clients or servers. Classes definitions are typically made network accessible through a web server.
使得classes能够通过网络访问
在这个步骤中,你可以为某一个类定义如何通过网络访问,如为远程接口以及它们关联的类型进行定义,为类的下载定义.类的定义是通过web服务器来实现网络访问的.
Starting the Application
Starting the application includes running the RMI remote object registry, the server, and the client.
The rest of this section walks through the steps used to create a compute engine.
开始应用程序
开始应用程序包括运行RMI远程对象注册,服务器端程序,以及客户端程序.章节的剩余部分将会贯穿这些步骤来创建一个计算引擎
Building a Generic Compute Engine
This trail focuses on a simple, yet powerful, distributed application called a compute engine. The compute engine is a remote object on the server that takes tasks from clients, runs the tasks, and returns any results.
The tasks are run on the machine where the server is running. This type of distributed application can enable a number of client machines to make use of a particularly powerful machine or a machine that has specialized hardware.
The novel aspect of the compute engine is that the tasks it runs do not need to be defined when the compute engine is written or started.
New kinds of tasks can be created at any time and then given to the compute engine to be run. The only requirement of a task is that its class implement a particular interface.
The code needed to accomplish the task can be downloaded by the RMI system to the compute engine. Then, the compute engine runs the task, using the resources on the machine on which the compute engine is running.
The ability to perform arbitrary tasks is enabled by the dynamic nature of the Java platform, which is extended to the network by RMI. RMI dynamically loads the task code into the compute engine's Java virtual machine and runs the task without prior knowledge of the class that implements the task.
Such an application, which has the ability to download code dynamically, is often called a behavior-based application. Such applications usually require full agent-enabled infrastructures. With RMI, such applications are part of the basic mechanisms for distributed computing on the Java platform.
构建一个一般的计算引擎
这份trail关注一个简单但很强大的分布式应用程序,它被称为计算引擎.计算引擎是一个存在于服务器上的远程对象,它可以接受客户端的任务,运行这些任务,并返回任何结果.这些任务与服务器运行在同一台机器上.这种类型的分布式应用程序可以使得多台客户端机器能够充分使用特别强大或有特殊硬件的计算机.
计算引擎的特别之处在于它运行的这些任务不必在计算引擎启动的时候被定义.新的任务可以在任何时候被创建,唯一的要求是这些必须实现某一个特别的接口.完成任务的代码能够通过RMI系统下载到计算引擎中.接下来,计算引擎就可以使用它的资源来运行这些任务.
能够执行任意任务的能力是通过Java平台的动态特性来实现的,这些特性继承自RMI.RMI动态地加载任务代码到计算引擎的Java虚拟机中,并以与先前任务实现无关的方式运行此任务.
像这样能够动态下载代码的应用程序,通常被称为基于行为的应用程序.
Writing an RMI Server
The compute engine server accepts tasks from clients, runs the tasks, and returns any results. The server code consists of an interface and a class. The interface defines the methods that can be invoked from the client. Essentially, the interface defines the client's view of the remote object. The class provides the implementation.
编写一个RMI服务器
计算引擎服务器从客户端接受任务,运行任务,并返回一些结果.服务端代码包括一个接口和一个类.此接口定义了能被客户端调用的方法,本质上来说,此接口定义了远程对象的客户视图.类则实现了此接口.
Designing a Remote Interface
This section explains the Compute interface, which provides the connection between the client and the server. You will also learn about the RMI API, which supports this communication.
定义远程接口
这个章节说明了Compute接口,此接口提供了客户端和服务器端的连接.你将了解到有关通信的RMI API.
Implementing a Remote Interface
This section explores the class that implements the Compute interface, thereby implementing a remote object.
This class also provides the rest of the code that makes up the server program, including a main method that creates an instance of the remote object, registers it with the RMI registry, and sets up a security manager.
实现远程接口
这个章节探索了实现Compute接口同时也即实现了远程对象的类.此类的剩余代码也展示了构建服务端程序,包括一个用于创建远程对象实例的main方法,使用RMI registry对实现类进行注册,以及设置一个安全管理器.
Designing a Remote Interface
At the core of the compute engine is a protocol that enables tasks to be submitted to the compute engine, the compute engine to run those tasks, and the results of those tasks to be returned to the client.
This protocol is expressed in the interfaces that are supported by the compute engine. The remote communication for this protocol is illustrated in the following figure.
设计远程接口
计算引擎的核心是一个协议,此协议使得任务能够被提交给计算引擎,然后交由计算引擎来运行这些任务,并将任务的计算结果返回给客户端.这个协议是通过被计算引擎支持的接口所表达的.协议的远程通信将会在下面的图形中阐明.
Each interface contains a single method. The compute engine's remote interface, Compute, enables tasks to be submitted to the engine.
The client interface, Task, defines how the compute engine executes a submitted task.
The compute.Compute interface defines the remotely accessible part, the compute engine itself. Here is the source code for the Compute interface:
每一个接口都包含一个单独的方法.计算引擎的远程接口Compute允许任务被提交给引擎.客户端接口Task,定义了怎样执行执行一个已经提交的任务.
compute.Compute接口定义了远程客户端能够访问的部分.以下是Compute接口的源码:
package compute;import java.rmi.Remote;import java.rmi.RemoteException;public interface Compute extends Remote { <T> T executeTask(Task<T> t) throws RemoteException;}
By extending the interface java.rmi.Remote, the Compute interface identifies itself as an interface whose methods can be invoked from another Java virtual machine.
Any object that implements this interface can be a remote object.
As a member of a remote interface, the executeTask method is a remote method. Therefore, this method must be defined as being capable of throwing a java.rmi.RemoteException.
This exception is thrown by the RMI system from a remote method invocation to indicate that either a communication failure or a protocol error has occurred.
A RemoteException is a checked exception, so any code invoking a remote method needs to handle this exception by either catching it or declaring it in its throws clause.
The second interface needed for the compute engine is the Task interface, which is the type of the parameter to the executeTask method in the Compute interface.
The compute.Task interface defines the interface between the compute engine and the work that it needs to do, providing the way to start the work.
Here is the source code for the Task interface:
通过继承java.rmi.Remote接口,Compute接口将其自身标识为其方法可以被来自另一个JVM中的对象所调用的接口.任何一个实现了此接口的对象都可以是一个远程对象.
作为远程接口的成员,executeTask方法是一个远程方法.因此,此方法必须声明抛出java.rmi.RemoteException异常.此异常是由RMI系统从一个远程方法调用中抛出的,用于表明此异常可能是由于通信失败或协议错误引起的.
RemoteException是一个受查异常,所以任何一个调用远程方法的代码都必须捕捉这个异常或者在它的throws子名中声明抛出这个异常.
计算引擎需要的第二个接口是Task接口,它是Compute接口中executeTask方法的参数化类型.compute.Task接口定义了任务所必须做的事情,并负责启动工作.
以下是Task接口的源码:
package compute;public interface Task<T> { T execute();}
The Task interface defines a single method, execute, which has no parameters and throws no exceptions. Because the interface does not extend Remote, the method in this interface doesn't need to list java.rmi.RemoteException in its throws clause.
The Task interface has a type parameter, T, which represents the result type of the task's computation. This interface's execute method returns the result of the computation and thus its return type is T.
The Compute interface's executeTask method, in turn, returns the result of the execution of the Task instance passed to it. Thus, the executeTask method has its own type parameter, T, that associates its own return type with the result type of the passed Task instance.
RMI uses the Java object serialization mechanism to transport objects by value between Java virtual machines. For an object to be considered serializable, its class must implement the java.io.Serializable marker interface. Therefore, classes that implement the Task interface must also implement Serializable, as must the classes of objects used for task results.
Different kinds of tasks can be run by a Compute object as long as they are implementations of the Task type. The classes that implement this interface can contain any data needed for the computation of the task and any other methods needed for the computation.
Here is how RMI makes this simple compute engine possible. Because RMI can assume that the Task objects are written in the Java programming language, implementations of the Task object that were previously unknown to the compute engine are downloaded by RMI into the compute engine's Java virtual machine as needed. This capability enables clients of the compute engine to define new kinds of tasks to be run on the server machine without needing the code to be explicitly installed on that machine.
The compute engine, implemented by the ComputeEngine class, implements the Compute interface, enabling different tasks to be submitted to it by calls to its executeTask method. These tasks are run using the task's implementation of the execute method and the results, are returned to the remote client.
Task接口中定义了一个没有参数且不抛出任何异常的execute方法.因为此接口不继承java.rmi.Remote接口,所以此接口中的方法不必在它的throws子句中列出java.rmi.RemoteException异常(即不抛出java.rmi.RemoteException异常).
Task接口有一个类型参数T,它用于表示任务计算的结果类型.此接口的exectue方法返回计算的结果,因此它的返回类型是T.Compute接口的executeTask方法,依次地,返回传递给它的Task实例的执行结果.因此,executeTask拥有它自己类型参数T,其返回的类型与传递的Task实例的返回类型相关.
RMI使用Java对象序列化机制,通过传值的方式在虚拟机之间传输对象.对于一个被认为是序列化的对象来说,其类必须实现java.io.Serializable标记接口.因此,实现Task接口的类也必须实现序列化接口,并且作为任务执行结果对象的类也必须实现序列化接口.
不同种类的任务可以被一个Compute对象执行,只要它们实现了Task类.实现这个接口的类可以包含任何与计算相关的数据和方法.
这里是RMI怎样使得计算引擎变为可能的原因.因为RMI假定用Java编写的Task对象实现了Task接口,此对象先前对于计算引擎来说是未知的,在JVM需要的时候,RMI就会将其下载到计算引擎的虚拟机中.
ComputeEngine类实现了Compute接口,允许不同的任务通过调用它的executeTask方法,将任务提交给它.任务的运行是通过Task的实现类的execute方法来完成的,并将结果返回给远程客户端.