JAVA RMI使用手册

原文地址:http://www.ihelloworld.cn/java/bac0554943737f6101437508af200000.html

This tutorial shows you the steps to follow to create a distributed version of the classic Hello World program using JavaTM Remote Method Invocation (Java RMI). While you work through this example, you will probably come up with a number of related questions. You are encouraged to look for answers in the Java RMI FAQ and the archives of the RMI-USERS mailing list. If you'd like to subscribe to the RMI-USERS mailing list, click here.
本教材将向你展示如何使用java rmi编写一个helloworld的分布式应用,当你编写这些例子的时候,可能会遇到很多相关的问题,我们鼓励你去Java RMI FAQ和RIM-USER邮件档案中寻找答案,如果你想订阅相关邮件,点击这儿。

The distributed Hello World example uses a simple client to make a remote method invocation to a server which may be running on a remote host. The client receives the "Hello, world!" message from the server.
这个分布式实例使用一个简单的客户端创建一个到远程服务器的RMI,客户端将从服务器上收到"Hello, world!"这个消息

This tutorial has the following steps:
    Define the remote interface
    Implement the server
    Implement the client
    Compile the source files
    Start the Java RMI registry, server, and client

本教程包含以下步骤:
    定义远程接口
    服务端实现
    客户端实现
    编译源码
    启动registry,服务端,客户端

The files needed for this tutorial are:
    Hello.java - a remote interface
    Server.java - a remote object implementation that implements the remote interface
    Client.java - a simple client that invokes a method of the remote interface

本教程需要的文件
    Hello.java - 远程接口
    Server.java - 远程接口的实现类
    Client.java - 客户端,用于回调远程接口的方法
    
Note: For the remainder of this tutorial, the terms "remote object implementation" and "implementation class" are used interchangeably to refer to the class example.hello.Server, which implements a remote interface.
注意:对于本教材余下部分,"remote object implementation"和"implementation class"都是指example.hello.Server这个类,他实现了远程接口

Define the remote interface
定义远程接口
    A remote object is an instance of a class that implements a remote interface. A remote interface extends the interface java.rmi.Remote and declares a set of remote methods. Each remote method must declare java.rmi.RemoteException (or a superclass of RemoteException) in its throws clause, in addition to any application-specific exceptions.

    远程对象是实现远程接口的一个实例,远程接口需继承java.rmi.Remote,并声明一系列远程方法,每个远程方法都应该抛出java.rmi.RemoteException(或者RemoteException的父类),以及程序可能抛出的其他异常
    Here is the interface definition for the remote interface used in this example, example.hello.Hello. It declares just one method, sayHello, which returns a string to the caller:
    这里是远程接口的定义,它只声明了一个方法,返回一个字符串给调用者
        package example.hello;

        import java.rmi.Remote;
        import java.rmi.RemoteException;

        public interface Hello extends Remote {
            String sayHello() throws RemoteException;
        }


    Remote method invocations can fail in many additional ways compared to local method invocations (such as network-related communication problems and server problems), and remote methods will report such failures by throwing a java.rmi.RemoteException.  For more information about the issues of failure and recovery in distributed systems, see A Note on Distributed Computing.
    远程方法调用(RMI)相比本地方法调用,会有更多的原因导致调用的失败(比如网络连接和服务器问题),如果RMI失败,会抛出java.rmi.RemoteException,有关分布式系统的跟多资料和失败原因讨论,请查看分布式计算上的资料
    
Implement the server
服务端实现
    A "server" class, in this context, is the class which has a main method that creates an instance of the remote object implementation, exports the remote object, and then binds that instance to a name in a Java RMI registry. The class that contains this main method could be the implementation class itself, or another class entirely.
    服务类,在本文中,这个类包含一个main方法,main方法用于创建远程对象实现类的实例,导出远程对象,并且通过名称将远程实例绑定到RMI注册器。这个main方法可以写在该类里面,也可以由其他类完成
    In this example, the main method for the server is defined in the class Server which also implements the remote interface Hello. The server's main method does the following:
    在这个例子中,main方法定义在了远程接口Hello的实现类Server里面,服务端的main方法如下:

        package example.hello;
                
        import java.rmi.registry.Registry;
        import java.rmi.registry.LocateRegistry;
        import java.rmi.RemoteException;
        import java.rmi.server.UnicastRemoteObject;
                
        public class Server implements Hello {
                
            public Server() {}

            public String sayHello() {
                return "Hello, world!";
            }
                
            public static void main(String args[]) {
                
                try {
                    Server obj = new Server();//创建对象
                    Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);//导出对象

                    // Bind the remote object's stub in the registry
                    Registry registry = LocateRegistry.getRegistry();//获取注册器
                    registry.bind("Hello", stub);//绑定对象

                    System.err.println("Server ready");
                } catch (Exception e) {
                    System.err.println("Server exception: " + e.toString());
                    e.printStackTrace();
                }
            }
        }

    The implementation class Server implements the remote interface Hello, providing an implementation for the remote method sayHello. The method sayHello does not need to declare that it throws any exception because the method implementation itself does not throw RemoteException nor does it throw any other checked exceptions.
    这个实现类实现了远程接口hello,提供已实现的远程方法sayHello。这个方法不需要声明任何异常,因为该方法实现本身并不会抛出RemoteException或者其他检查期异常
    Note: A class can define methods not specified in the remote interface, but those methods can only be invoked within the virtual machine running the service and cannot be invoked remotely.
    实现类可以定义接口未指定的方法,但是这个方法只能在本地虚拟机中调用,不能通过远程调用。
    Create and export a remote object
    创建并导出远程对象
    The main method of the server needs to create the remote object that provides the service. Additionally, the remote object must be exported to the Java RMI runtime so that it may receive incoming remote calls. This can be done as follows:
    服务类的main方法需要创建用于提供远程服务的对象,另外,远程对象必须导入到RMI运行时,这样他才可以接受远程调用,代码如下:
        Server obj = new Server();
        Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);


    The static method UnicastRemoteObject.exportObject exports the supplied remote object to receive incoming remote method invocations on an anonymous TCP port and returns the stub for the remote object to pass to clients. As a result of the exportObject call, the runtime may begin to listen on a new server socket or may use a shared server socket to accept incoming remote calls for the remote object. The returned stub implements the same set of remote interfaces as the remote object's class and contains the host name and port over which the remote object can be contacted.

    静态方法UnicastRemoteObject.exportObject在一个匿名的TCP端口导出提供RMI的远程对象,返回一个远程对象的桩(stub)与客户端交互,调用exportObject之后,运行时在一个新服务套接字或者使用共享服务套接字上开始监听接受远程调用,返回的桩同样实现远程接口,并且包含主机名和端口,以便与远程对象连接
    
    Note: As of the J2SE 5.0 release, stub classes for remote objects no longer need to be pregenerated using the rmic stub compiler, unless the remote object needs to support clients running in pre-5.0 VMs. If your application needs to support such clients, you will need to generate stub classes for the remote objects used in the application and deploy those stub classes for clients to download. For details on how to generate stub classes, see the tools documentation for rmic [Solaris, Windows]. For details on how to deploy your application along with pregenerated stub classes, see the codebase tutorial.
     Register the remote object with a Java RMI registry
    在J2SE5.0以后,远程对象stub classes不需要通过rimc命令预编译,除非你需要支持5.0之前的虚拟机。如果你的应用需要支持这样的客户端,你需要在你的应用中给你的远程对象创建stub classes,并部署这些stub classes,以便客户端下载。关于产生stub classes的详细信息,参考rmic使用文档,关于如何部署stub classes的信息,请参考codebase tutorial.
    
    For a caller (client, peer, or applet) to be able to invoke a method on a remote object, that caller must first obtain a stub for the remote object. For bootstrapping, Java RMI provides a registry API for applications to bind a name to a remote object's stub and for clients to look up remote objects by name in order to obtain their stubs.
    对于需要调用远程对象的调用者,调用者必须先获得远程对象的stub.Java RMI给应用提供注册API用于绑定远程对象的stub,同时为客户端搜索获取stub.
    A Java RMI registry is a simplified name service that allows clients to get a reference (a stub) to a remote object. In general, a registry is used (if at all) only to locate the first remote object a client needs to use. Then, typically, that first object would in turn provide application-specific support for finding other objects. For example, the reference can be obtained as a parameter to, or a return value from, another remote method call. For a discussion on how this works, please take a look at Applying the Factory Pattern to Java RMI.
    Java RMI注册器是一个简化的命名服务,他允许客户端引用远程对象,一般情况下,只有在第一次载入远程对象的时候需要使用它,然后,第一个对象可以查找其他的对象,例如:远程方法的参数,或者返回值使用了远程对象,或者该方法调用了其他远程方法,都会导致其他远程对象的加载
    Once a remote object is registered on the server, callers can look up the object by name, obtain a remote object reference, and then invoke remote methods on the object.
    一旦远程对象在服务器上注册,调用者就可以通过命名搜索他们,并获得远程对象的引用,然后执行目标的远程方法
    The following code in the server obtains a stub for a registry on the local host and default registry port and then uses the registry stub to bind the name "Hello" to the remote object's stub in that registry:
    下面的代码在本机和默认端口上获得stub,并以Hello注册该stub
        Registry registry = LocateRegistry.getRegistry();
        registry.bind("Hello", stub);


    The static method LocateRegistry.getRegistry that takes no arguments returns a stub that implements the remote interface java.rmi.registry.Registry and sends invocations to the registry on server's local host on the default registry port of 1099. The bind method is then invoked on the registry stub in order to bind the remote object's stub to the name "Hello" in the registry.
    未带参数的静态方法LocateRegistry.getRegistry返回接口java.rmi.registry.Registry的实现,获取本机,端口默认为1099的注册器。方法bind通过名字“Hello”注册远程对象的stub
    Note: The call to LocateRegistry.getRegistry simply returns an appropriate stub for a registry. The call does not check to see if a registry is actually running. If no registry is running on TCP port 1099 of the local host when the bind method is invoked, the server will fail with a RemoteException.
    调用LocateRegistry.getRegistry 只是简单的从注册器返回相关的stub,该方法并不会检查注册器是否运行,如果调用bind方法的时候本机的1099端口没有注册器,服务器会抛出RemoteException
    
    Implement the client
    客户端实现
    The client program obtains a stub for the registry on the server's host, looks up the remote object's stub by name in the registry, and then invokes the sayHello method on the remote object using the stub.
    客户端通过命名从服务器注册器上获得远程对象的stub,然后利用stub调用远程对象的sayHello方法
    Here is the source code for the client:
    这里是客户端的源码:
        package example.hello;

        import java.rmi.registry.LocateRegistry;
        import java.rmi.registry.Registry;

        public class Client {

            private Client() {}

            public static void main(String[] args) {

                String host = (args.length < 1) ? null : args[0];
                try {
                    Registry registry = LocateRegistry.getRegistry(host);
                    Hello stub = (Hello) registry.lookup("Hello");
                    String response = stub.sayHello();
                    System.out.println("response: " + response);
                } catch (Exception e) {
                    System.err.println("Client exception: " + e.toString());
                    e.printStackTrace();
                }
            }
        }

    This client first obtains the stub for the registry by invoking the static LocateRegistry.getRegistry method with the hostname specified on the command line. If no hostname is specified, then null is used as the hostname indicating that the local host address should be used.
    这个客户端首先通过LocateRegistry.getRegistry方法获得stub,该方法的参数hostname由命令行指定,如果hostname没有指定,则hostname为空,意味着本机地址将会使用
    Next, the client invokes the remote method lookup on the registry stub to obtain the stub for the remote object from the server's registry.
    然后,客户端会调用lookup方法获得远程对象的stub
    Finally, the client invokes the sayHello method on the remote object's stub, which causes the following actions to happen:
    最后,客户端通过远程对象的stub调用sayHello方法,将会触发如下动作
        The client-side runtime opens a connection to the server using the host and port information in the remote object's stub and then serializes the call data.
        客户端打开一个到服务端的连接,连接的信息存储与stub中,然后stub将调用数据序列化到服务端
        The server-side runtime accepts the incoming call, dispatches the call to the remote object, and serializes the result (the reply string "Hello, world!") to the client.
        服务端接受进入的调用,转发到相应的远程对象,然后序列化结果(回复字符串“Hello, world!”)到客户端
        The client-side runtime receives, deserializes, and returns the result to the caller.
        客户端接受,反序列化,返回结果给调用者
     The response message returned from the remote invocation on the remote object is then printed to System.out.
    回复字符串从远程调用中返回,然后通过System.out输出
Compile the source files
编译源文件
    The source files for this example can be compiled as follows:
    编译命令如下
        javac -d destDir Hello.java Server.java Client.java

    where destDir is the destination directory to put the class files in.
    destDir是class的输出目录
    Note: If the server needs to support clients running on pre-5.0 VMs, then a stub class for the remote object implementation class needs to be pregenerated using the rmic compiler, and that stub class needs to be made available for clients to download. See the codebase tutorial for more details.
    注意: 如果服务器需要支持5.0以下的客户端,stub 的类文件需要使用rmic编译器提前生成,并使该stub类文件能够被客户端下载(比如以http方式将该class文件发布),详情请参考codebase教程
Start the Java RMI registry, server, and client
启动Java RMI注册器,服务端,客户端
    To run this example, you will need to do the following:
        Start the Java RMI registry
        Start the server
        Run the client

     Start the Java RMI registry
    
    To start the registry, run the rmiregistry command on the server's host. This command produces no output (when successful) and is typically run in the background. For more information, see the tools documentation for rmiregistry [Solaris, Windows].
    启动RMI注册器:在服务器主机命令行下运行rmiregistry命令,这个命令在成功的时候没有输出,通常也运行于后台。更多资料,参考rmiregistry文档
    For example, on the Solaris(tm) Operating System:
    例如,在Solaris系统下命令
        rmiregistry &

    Or, on Windows platforms:
    Windows系统下命令
        start rmiregistry
    提示: 该命令必须在项目的class输出目录中执行,否则运行服务端的时候会报Hello类找不到。
     By default, the registry runs on TCP port 1099. To start a registry on a different port, specify the port number from the command line. For example, to start the registry on port 2001 on a Windows platform:
    默认情况下,注册器运行于tcp 1099端口。如果需要使用其他的端口,在命令行中指定端口号即可。如下
        start rmiregistry 2001

    If the registry will be running on a port other than 1099, you'll need to specify the port number in the calls to LocateRegistry.getRegistry in the Server and Client classes. For example, if the registry is running on port 2001 in this example, the call to getRegistry in the server would be:
    如果注册器不是运行在1099端口,LocateRegistry.getRegistry 需要指定端口
        Registry registry = LocateRegistry.getRegistry(2001);
    提示:该行代码获取注册器,即我们刚才手动启动的rmiregistry,我们可以不用手动启动,在代码中启动
        Registry registry = LocateRegistry.createRegistry(2001);
    Start the server

    To start the server, run the Server class using the java command as follows:
    启动服务端
    On the Solaris Operating System:

        java -classpath classDir -Djava.rmi.server.codebase=file:classDir/ example.hello.Server &

    On Windows platforms:

        start java -classpath classDir -Djava.rmi.server.codebase=file:classDir/ example.hello.Server

    where classDir is the root directory of the class file tree (see destDir in the section "Compiling the source files"). Setting the java.rmi.server.codebase system property ensures that the registry can load the remote interface definition (note that the trailing slash is important); for more information about using this property, see the codebase tutorial.
    classDir是class文件的根目录,java.rmi.server.codebase系统属性是确保注册器可以加载远程接口定义文件(这个很重要),更多信息,参考codebase手册
    
    提示: 这个地方不太明白,之前不是在在class文件根目录执行rmiregistry命令,然后按如上启动服务端,会报Hello类找不到,按上面那句话的意思-Djava.rmi.server.codebase=file:classDir/就是确保rmiregistry能加载Hello接口类。后来我在class文件根目录执行rmiregistry命令,再启动服务器就没有错,而且去掉java.rmi.server.codebase参数也不会有错。
    The output from the server should look like this:
    服务端将输出如下
        Server ready

    The server remains running until the process is terminated by the user (typically by killing the process).
     Run the client
    服务端会一直运行,除非用户结束该进程。
    Once the server is ready, the client can be run as follows:
    服务端启动后,运行客户端
        java  -classpath classDir example.hello.Client
     提示:在这个例子中,直接按如上执行是没有问题的,但是在某些情况下是有问题的(比如sayHello返回的不是字符串,而返回一个接口,会报实现类找不到),以我的理解,这里是应该加上-Djava.rmi.server.codebase=file:classDir/的,以便客户端jvm能够从远程加载返回接口的实现类
    where classDir is the root directory of the class file tree (see destDir in the section "Compiling the source files").

    The output from the client is the following message:
    客户端输出如下
        response: Hello, world!
        
    有些网上的例子会和这个不一样,对初学者来说会很迷茫,这里就说几点。
     1、启动注册器问题,有些例子明确写了要在命令行下启动注册器(这个例子也是),但有些没有,没有启动的并不是真的没有启动,只是在JAVA代码中启动的,不是自己去命令行中启动。
        Registry registry = LocateRegistry.createRegistry(2001);就代替了手动到命令行中启动注册器。
    2、使用rmic命令产生stub和skeleton类,这里也有所提及,但并没有这一步操作,这里也讲了,5.0以下是需要此操作的,但没将为什么,这是因为以后的版本有动态代理的功能,java会动态产生远程接口的stub和skeleton类(代理类)。

    3、java.rmi.server.codebase的功能,这个可以去看相关文档,这里简单的说一下,jvm加载class分为本地和远程加载,classpath规定的就是jvm加载本地class的位置,codebase规定的是jvm加载远程class的位置



   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值