`

RMI 入门

rmi 
阅读更多

RMI : remote method invocation,远程方法调用。

 

有Client object, client helper(Stub), Service Helper(Skeleton), service object四个参与者。前两个是客户端,后两个是服务器端的,两个helper都是用于对信息的包装(pack)和解包(unpack),以方便网络传输。

 

五个步骤:

一,make a remote interface(MyService.java)。定义服务器端提供的方法。远程客户就是调用这个接口的方法。Stub和Skeleton都会实现这个接口。

 

二,make a remote implementation(MyServiceImpl.java)。这是实际的服务类,实际的操作由这个类来实现。

 

三,generate the stubs and skeletons using rmic。在cmd下输入: rmic MyServiceImpl,会产生MyServiceImpl_Stub.class 和 MyServiceImpl_Skel.class两个类文件,rmic是jdk自带的一个工具。

 

四,start the RMI registry(rmiregistry),在cmd输入:rmiregistry,启动这项服务后,客户端就可以通过这项服务找对对应的Proxy/client helper(stub)。

 

五,start the remote service。cmd输入:java MyServiceImpl,就会在rmi registry里注册这个服务,令这服务可以被远程调用。

 

以建立一个向服务器端请求获取一个String为实例,具体说明:

初次尝试可以在同一机器上进行,但在实际应用中,应在不同机器上,才能体现rmi的用处

 

在服务器端的操作:

1.建立远程接口。

package chapter11.proxy.rmi;

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

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

 说明:远程接口,是客户端调用服务器端的接口,就是这个接口提供什么方法,那客户端也就只能调用远程对象的这些方法。接口需要继承Remote类,查看Remote类源码,可知它只是个空的接口,没任何方法在里面,在rmi调用中,这是作为一个标志接口("marker" interface)。另外每个远程方法都必须抛出RemoteException,因为每个远程方法都被认为是不安全的。还有远程方法的返回值和参数的类型必须是可序列化的(serializable,int,char,long等所有原始类型和jdk自带的集合类型都是可序列化,如果是自己创建的类,就必须extendsSerializable接口)。本例中的sayHello()的返回值String是可序列化的。

 

2.实现远程接口。

package chapter11.proxy.rmi;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;


public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{

	public MyRemoteImpl() throws RemoteException{
	}
	
	@Override
	public String sayHello() throws RemoteException {
		return "server syas, 'hey'";
	}
	
	public static void main(String[] args) {
		try{
			MyRemote service = new MyRemoteImpl();
			Naming.rebind("RemoteHello", service);
		}catch(Exception e){
			e.printStackTrace();
		}
	}

}

 新建一个类实现远程接口。继承UnicastRemoteObject 是为了继承一些作为一个远程服务对象的功能,该类提供四个静态方法:

RemoteStub exportObject(Remote obj);(其中一个),可导出(export)远程对象。

另外因为UnicastRemoteObject类的构造器抛出RemoteException,所以其子类的构造器都需要抛出同样的异常(Java语法所定,因为实例化子类前,jvm会实例化其父类)。

main方法是在Rmi Registry里注册这个服务的,Main方法可以放到另外一个类里执行,不一定要放要实现类里,这里为了演示简单而放这。

注册通过java.rmi.Naming类的静态方法rebing(String name, Remote obj)。name是客户端获取的服务的名称,obj是service类。

 

3.生成stubs和skeleton(在Jdk5以后,默认是-v1.2[rmic 的stub协议版本],只会生成stub)。

这一步,需要要用到jdk自带的工具:rmic。先确定你系统的Path环境变量里已经设了%java_home%\bin(或不设,就把工具的路径全称写全)。rmic命令如下说明:

 

rmic 参数: 
-classpath <path> 指定查找输入类文件的位置; 
-d <dir> 指定存放生成文件的位置

对于没放在package里的类:直接进入.class目录,
运行 rmic MyRemoteImpl,会在.class目录生成xx_stub.class文件

对于package chapter11.proxy.rmi下的MyRemoteImpl.class。(本例子就是这情况)
就是bin\chapter11\proxy\rmi\MyRemoteImpl.class。(在eclipse的.class文件默认放在bin目录)
进入对应的bin目录,运行:rmic chapter11.proxy.rmi.MyRemoteImpl


就会在bin\chapter11\proxy\rmi\目录下生成xx_stub.class文件。

可以通过 -d <dir>,配置生成stub的目录,但生成的目录是上层目录,在dir下会
根据Package信息,自动创建目录层次:dir\chapter11\proxy\rmi\xx_stub.class。
 

我是开始以为对.java文件调用rmic。在src目录下(eclipse建的工程),试过很多次后,发现我错了,原来是要cmd进入到.class的bin目录里调用rmic命令。并且rmic 命令中的文件名不需要.class后缀。调用后会生成

(总之,在这里提出一点很重要的,所有的命令都要在bin目录下执行 ,这样才能保证不浪费时间ClassNotFound之类的错误,我在cmd下运行命令时,由于有时切换到不同目录,导致浪费很多时间在尝试。。。人生苦短啊,大家别浪费时间在低级错误上)

 

4.生成xx_stub.class文件后(stub.class应该在bin目录中,与MyRemoteImpl同一目录),在bin目录中执行命令:

 rmiregistry

这应该在一个新的终端执行。这就在服务器端启动了一个“注册管理器”(我这样称呼的,书上是这样描述的:RMI registry(on server))。“注册管理器”是负责管理各种服务对象的(MyRemoteImpl)和返回对应stub对象。

 

5.启动服务。在cmd运行: java chapter11.proxy.rmi.MyRemoteImpl 。记住是在bin目录里运行该命令。

 

6.好了,最后就写客户端代码来测试。(接下来的代码,可以在客户端写,不需要在服务器端写,但在客户端需要MyRemote.class和MyRemoteImpl.class两个类文件)

package chapter11.proxy.rmi;

import java.rmi.Naming;

public class MyRemoteClient {
	public static void main(String[] args) {
		try {
			MyRemote service = (MyRemote) Naming
					.lookup("rmi://127.0.0.1/RemoteHello");
			String s = service.sayHello();
			System.out.println(s);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}

对于客户端代码,首先通过java.rmi.Naming的lookup(url)方法来找到服务器的服务对象,接着rmi registry返回该对象,这样客户端就保存一个stub(服务对象,相当于MyRemoteImpl的引用) ,客户端这时就可以调用服务对象的方法sayHello()来返回结果。

 

以下是三个cmd的内容:

rmi registry:

D:\My Documents\chow\workspace\headfirstDf\bin>rmiregistry

 

server:(客户端调用两次,所以打印两次request...)

D:\My Documents\chow\workspace\headfirstDf\bin>java chapter11.proxy.rmi.MyRemoteImpl
get a sayHello request...
get a sayHello request...
 

client:

D:\My Documents\chow\workspace\headfirstDf\bin>java chapter11.proxy.rmi.MyRemote
Client
server syas, 'hey'

D:\My Documents\chow\workspace\headfirstDf\bin>java chapter11.proxy.rmi.MyRemote
Client
server syas, 'hey'

 

geek bits:

这里提到客户端需要服务器端的class文件,可以简单地从服务器端发送到客户端。

在jdk5后,还有一种方法,叫“动态类下载”(dynamic class downloading)。客户端通过url来找到class文件,会通过http的get来获取类文件。

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics