`
yelangchs
  • 浏览: 11083 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

远程调用RMI

    博客分类:
  • Java
阅读更多

一.客户与服务器角色

在传统的客户/服务器模式中,客户请求服务器。服务器解析传输的请求,并得到响应,再传给客户端。但是这样必须考虑中间传输格式的解析。如果客户端无须关心请求的传输和解析,只调用本地方法的形式得到结果,但是有时提供服务的对象不再同一个虚拟机内,甚至不是java虚拟机,那怎么办,解决的办法是在客户端提供一个服务器的代理,客户直接调用这个代理,这个代理负责客户与服务器交流。同样,在服务器也安装一个代理负责服务器与客户端通信。并以常规方式调用服务器方法。接下来的问题就是代理之间是怎么通信的呢。

RMI----------------Java远程方法调用技术,支持java的分布式对象之间的调用

CORBA------------通用对象请求架构,支持任何编程语言编写的对象之间的方法调用,CORBA使用Internet inter-ORB协议(IIOP)支持对象间的通信

SOAP--------------简单对象访问协议,它也独立编程语言。基于XML的传输格式。

另外,MicroSoft也使用另一种底层协议COM支持对象间通信。

CORBA和SOAP都是独立于语言的,客户端和服务器完全使用C,C++,Java来编写,只要你提供一种接口描述,以说明你的对象能够处理的方法的签名和数据类型。该接口描述是一种特殊的描述语言,对于CORBA来说接口定义语言是(IDL),对于SOAP则是Web服务描述语言(WSDL)。

CORBA更加高效,而SOAP更适合Web架构的系统。如果通信的对象都是Java实现的,那么最好使用RMI。

 

二.远程方法调用

客户端对象------------------一般是发起远程调用的对象

服务器对象------------------相应的远程对象

2.1 存根和参数编组

存根-------------当客户端代码调用一个远程方法时,实际上是调用的一个本地方法,我们称此代理对象为存根,存根位于客户端上,而不是服务器上,存根将调用远程方法所需的参数打包成一组字节,这个打包过程是与硬件无关的编码的编码方式进行编码。

参数编组--------对参数编码的过程叫参数编组,它的目的是将参数转换成适合在虚拟机传输的格式。

总之,客户端的存根构造一个信息块,它由以下几部分组成:

  • 被使用的远程对象的标示符
  • 被调用的方法的描述
  • 编组后的参数

然后将这个信息块发送到服务器,服务器接受对象为每个远程方法执行以下动作

  • 反编组参数
  • 定位要调用的对象
  • 调用所需的方法
  • 捕获返回值或者调用产生的异常,对它进行编组;
  • 对返回值编组,打包送回客户端存根

客户端存根对接受到得返回值编组进行反编组,如果远程方法抛出异常,客户端存根在客户端处理空间中重新抛出异常。

2.2 动态类加载

当一个远程对象,作为远程方法的参数或者返回值,传递给另一个程序时,该程序很显然需要这个对象的Class文件。

 

三.配置远程方法调用

首先,你必须在客户端和服务器同时运行程序,其次,必要的对象信息被划分为客户端接口和服务器实现。还有专门的查询机制,能够使得客户端准确定位服务器上的对象

3.1 接口与实现

客户端可能需要操纵服务器对象,但是实际上它并不需要真正的服务器对象的引用,因为它只要知道这个对象时干什么的就可以了,我们可以使用接口的方式来描述它能做什么。而这些接口是客户端和服务器共享的,它必须同时存在于客户端和服务器。注意的是远程对象的接口必须继承与Remote。而且必须抛出RemoteException异常。

package rmi;

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

public interface Product extends Remote {
	String getDescription() throws RemoteException;
}

 

然后呢,在服务器必须实现这个接口

服务器类通常继承RemoteServer。

package rmi;

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

public class ProductImpl extends UnicastRemoteObject implements Product {

	private String desc;
	
	public ProductImpl(String desc) throws RemoteException {
		super();
		this.desc = desc;
	}

	@Override
	public String getDescription() throws RemoteException {
		// TODO Auto-generated method stub
		return "I am a "+desc+"buy me!";
	}

}

 

3.2 存根类生成

从JDK5.0开始,存根类可以自动生成,但是在这之前,存根类必须手动生成:

rmic -v1.2 ProductImpl

它会生成两个文件ProductImpl_Stub.class和ProductImpl_Skel.class

3.3 定位服务器对象

要访问服务器的远程对象,必须获得一个客户端存根,最直接的方法时调用远程方法,获得返回值返回存根,但是这样第一个远程对象怎么获得呢。RMI类库提供了自举注册服务来定位第一个服务器对象。通过给自举注册服务提供一份对象的引用和名字,就可以注册服务器对象,客户端就可以获得对象的存根。这些对象的名称最好唯一。

 

package rmi;

import java.rmi.RemoteException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class ProductServer {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			System.out.println("Constructing server implementations...");

			ProductImpl p1=new ProductImpl("boy");
			
			ProductImpl p2=new ProductImpl("girl");
			
			System.out.println("Binding server implementations to registry...");
			Context namingContext = new InitialContext();
			
			namingContext.bind("rmi:b",p1);
			namingContext.bind("rmi:g",p2);
			
			System.out.println("Waiting for invocation from clients....");
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NamingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

}

 

1.启动服务器(windows)

start rmiregistry

start java ProductServer

2.列举远程对象

NamingEnumeration<NameClassPair> e = namingContext.list("rmi:");

package rmi2.ShowBindings;


/**
   @version 1.10 2004-08-14
   @author Cay Horstmann
*/

import java.rmi.*;
import java.rmi.server.*;
import javax.naming.*;

/**
   This programs shows all RMI bindings.
*/
public class ShowBindings
{  
   public static void main(String[] args)
   {  
      try
      {  
         Context namingContext = new InitialContext();
         NamingEnumeration<NameClassPair> e = namingContext.list("rmi:");
         while (e.hasMore())
            System.out.println(e.next().getName());
      }
      catch (Exception e)
      {  
         e.printStackTrace();
      }
   }
}

 

3.4 编写客户端代码

使用RMI的客户端必须安装一个安全管理器,用以动态加载存根的行为。

System.setSecurityManager(new RMIsetSecurityManager)

package rmi2.Product;

/**
   @version 1.20 2004-08-15
   @author Cay Horstmann
*/

import java.rmi.*;
import java.rmi.server.*;
import javax.naming.*;

/**
   This program demonstrates how to call a remote method
   on two objects that are located through the naming service.
*/
public class ProductClient
{  
   public static void main(String[] args)
   {  
      System.setProperty("java.security.policy", "client.policy");
      System.setSecurityManager(new RMISecurityManager());
      String url = "rmi://localhost/";
         // change to "rmi://yourserver.com/" when server runs on remote machine yourserver.com
      try
      {  
         Context namingContext = new InitialContext();
         Product c1 = (Product) namingContext.lookup(url + "toaster");
         Product c2 = (Product) namingContext.lookup(url + "microwave");         

         System.out.println(c1.getDescription());
         System.out.println(c2.getDescription());
      }
      catch (Exception e)
      {  
         e.printStackTrace();
      }
   }
}

 

要允许客户端连接RMI注册表以及服务器对象,必须提供一个策略文件。服务器对象使用的是大于1024的端口,RMI默认端口是1099.

client.policy

grant 
{  
   permission java.net.SocketPermission 
      "*:1024-65535", "connect,accept";
   permission java.net.SocketPermission 
      "localhost:80", "connect";
};

 总之。

  • 编译接口,实现类,服务器类,客户端类等文件
  • 启动RMI注册表     start rmiregistry

 对于RMI的部署相当的麻烦,一般初学者很容易出错,我也没有深入研究学习,在这里我也不说了。有兴趣的人可以上Google搜索。

 

四.远程方法的参数传递

4.1 传递非远程对象

  当一个不是远程对象的对象,需要从一个java虚拟机传送另外一个java虚拟机时,第一个java虚拟机会制作一个对象COPY,然后通过网络传送到另外一个虚拟机,当你将对象传递到本地方法的是,传递的只是引用。

所以远程对象通过存根传递的,非远程对象通过复制传递。

无论何时,当调用远程方法的时候,存根将所有参数值的副本打包,发给服务器,其中用到了对象序列化机制编组参数。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics