Java RMI (1)

阅读更多
1. Java RMI
Java Remote Method Invocation(Java远程方法调用)允许程序员创建分布式Java技术为基础的应用,可以调用不同JVM远程对象上的远程方法。RMI使用对象序列化去marshal(编码),unmarshal(解码)参数进行,不截断类型,支持面向对象的多态性。

2. Stub
Java RMI (1)_第1张图片

  RMI相当于一个代理模式。客户对象想要调用远程服务对象上的远程方法,我们需要一些辅助对象。这些辅助的对象使客户就像在调用本地对象的方法一样。客户对象调用客户辅助对象上的方法,仿佛客户辅助对象就是真正的服务。而客户辅助对象负责为我们转发这些请求,但是客户辅助对象不是真正的远程服务。虽然操作很像,但是不具有真正远程服务对象的方法逻辑。在服务端,服务辅助对象从客户辅助对象中接受请求,将调用的信息解包,然后调用真正服务对象上的真正方法。服务辅助对象从服务中得到返回值,将它打包,然后返回客户辅助对象,客户服务对象对信息解包,最后将返回值交给客户端。
  Stub(存根)就相当于这里的客户辅助对象。存根将所需要的参数进行编码后传递给远程方法,并将调用结果或者异常传递给客户端。从JDK1.5开始,存根类可以采用动态代理的方式自动生成。在这之前,必须使用rmic工具手动创建。

3. java.rmi.Remote
  3.1 扩展java.rmi.Remote接口
    Remote是一个标记接口,没有任何方法,所有远程服务对象必须间接或者直接的扩展Remote接口。由于远程服务对象可以被其他的JVM调用,所有可以实现一个或多个远程接口。如果远程服务对象被同时的访问,必须在其实现中保证线程的安全。
  
   public interface MyRemote extends java.rmi.Remote{}

   3.2 接口声明中的所有方法都将抛出RemoteException异常。
     客户调用远程接口的stub上的方法,而stub底层调用了网络和I/O,所以可能会发生各种异常,所以必须声明远程异常来解决。java.rmi.RemoteException继承自java.io.IOException。
  
   public interface MyRemote extends Remote{
      public String sayHello() throws RemoteException;
    }

   3.3 远程方法的变量和返回值必须是原语类型或者是可序列化类型。
     这不难理解,因为远程方法的变量必须被打包并通过网络发送。(所以,在传送自己定义的类时,必须保证实现Serializable接口)

4. java.rmi.RemoteException
  RemoteException继承自java.io.IOException。它是一个检查型异常,通常用来标识:通信失败、远程调用参数/返回值在编码(marshalling)/解码(unmarshalling)过程中出错,协议错误。

5. java.rmi.server.RemoteObject
Java RMI (1)_第2张图片

  RemoteObject类实现远程对象的java.lang.Object 行为,并提供了远程对象默认的hashCode、equals 和 toString 方法实现。创建远程对象并将其导出(使远程对象具有某些"远程的"功能)所需的方法由类UnicastRemoteObject 和 Activatable 提供。

6. java.rmi.registry.Registry
  Registry是简单远程对象注册表的一个远程接口(默认的端口号为1099
),它提供绑定和查找的功能。其内部维护了一个名到对象的映射。LocateRegistry 用于获得对特定主机(包括本地主机)上引导远程对象注册表的引用,或用于创建一个接受对特定端口调用的远程对象注册表。 注意,getRegistry 调用并不实际生成到远程主机的连接。它只创建对远程注册表的本地引用,即便远程主机上没有正运行的注册表,它也会成功创建一个引用。LocateRegistry.createRegistry()方法在本机指定的端口上创建并发布一个registry远程对象。

7. 示例程序:
  7.1 定义远程接口
public interface MyRemote extends Remote {
	public String sayHello() throws RemoteException;
}

  7.2 实现远程接口
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{

	private static final long serialVersionUID = -3510887331988311760L;

	protected MyRemoteImpl() throws RemoteException {
	}  //这里必须抛出RemoteException异常,因为超类会抛出这个异常

	@Override
	public String sayHello() throws RemoteException {
		return "server say hello to client";
	}
}


  7.3 用RMI Registry注册此服务
  
  public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{

	private static final long serialVersionUID = -3510887331988311760L;

	protected MyRemoteImpl() throws RemoteException {
	}

	@Override
	public String sayHello() throws RemoteException {
		return "server say hello to client";
	}
	
	public static void main(String[] args) {
		try {
			MyRemote remote = new MyRemoteImpl();
			Naming.bind("myRemoteServer", remote);
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (AlreadyBoundException e) {
			e.printStackTrace();
		}
		
	}
}

当注册这个实现对象时,RMI系统实际注册的是stub。

   7.4 客户端定义
public class Client {
	public static void main(String[] args) {
		try {
			MyRemote remote = (MyRemote)Naming.lookup("rmi://127.0.0.1/myRemoteServer");
			System.out.println(remote.sayHello());
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}
		
	}
}


启动:
  1. 启动remiregistry服务(位于Java\jdk1.6.0_18\bin,启动的目录必须为你workspace的bin目录)
rmiregistry -J-Djava.rmi.server.codebase=file:/D:/FXworkspace/rmi/bin/

  2. 执行MyRemoteImpl
  3. 执行Client

结果输出:
server say hello to client    
 
工作方式:

Java RMI (1)_第3张图片
  1)客户到RMI registry中寻找
  2)RMI registry返回stub对象。
  3)客户调用stub方法,就像stub是真正的服务对象一样。


注:rmic是JDK内的一个工具,用来为一个服务类产生stub和skeleton。rmic有一些选项可以调整,包括不要产生skeleton、查看源代码、甚至使用IIOP作为协议。
  • Java RMI (1)_第4张图片
  • 大小: 24.6 KB
  • Java RMI (1)_第5张图片
  • 大小: 19.2 KB
  • Java RMI (1)_第6张图片
  • 大小: 27.5 KB
  • 查看图片附件

你可能感兴趣的:(Java,ITeye,网络应用,网络协议,JVM)