论坛首页 入门技术论坛

Java分布式之RMI实例教程

浏览 12177 次
该帖已经被评为新手帖
作者 正文
   发表时间:2010-08-22   最后修改:2011-02-28
Java分布式之RMI实例教程

Peter Wei
2010-8-22


前言
   最近的联通项目,下一阶段可能会涉及到和各省间的RMI接口,所以总结一下08年中国移动自动拨测系统用到的RMI技术,以备不时之需。同时也给广大初哥提供一些学习资料,哈哈。前几年,一直忙于项目,没怎么做系统总结。以后计划写一些以前项目用过的Java分布式技术实例教程,如:RMI、Socket、Mina、SNMP、SOAP、Web Service、Hessian、JMS等。希望和大家一起交流,分享经验,一起提高。

RMI简介
RMI,远程方法调用(Remote Method Invocation)是Enterprise JavaBeans的支柱,是建立分布式Java应用程序的方便途径。RMI是非常容易使用的,但是它非常的强大。
RMI的基础是接口,RMI构架基于一个重要的原理:定义接口和定义接口的具体实现是分开的。看看jboss-remoting:


基本原理
   要实现网络机器间的通讯,首先得来看看计算机系统网络通信的基本原理,在底层层面去看,网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络IO来实现,其中传输协议比较出名的有http、tcp、udp等等,http、tcp、udp都是在基于Socket概念上为某类应用场景而扩展出的传输协议,网络IO,主要有bio、nio、aio三种方式,所有的分布式应用通讯都基于这个原理而实现,只是为了应用的易用,各种语言通常都会提供一些更为贴近应用易用的应用层协议。

主要步骤
  分为以下几个步骤:
1. 创建远程接口及声明远程方法(RmiMonitorService.java)
2. 实现远程接口及远程方法(继承UnicastRemoteObject)(RmiMonitorServiceImpl.java)
3. 启动RMI注册服务,并注册远程对象(RmiServer.java)
4. 客户端查找远程对象,并调用远程方法(MonitorClient.java)
5. 运行实例

业务场景
在移动拨测系统管理端中要融合实时显示。简单点说就是设备出现告警时,要采用不同方式实时展示。如Web界面(Ajax)、GIS等。

   主要业务流程设计:
1. 设备告警
2. 调用RMI Client
3. 调用RMI Server
4. 调用业务处理接口
5. 告警信息入库
6. 实时显示(Ajax,Gis等技术)


技术设计
接口函数函数名称: int interactive( int funindex, string param )
参数说明:
funindex 功能号,整型。1为设备告警
param 交互参数,字符串型。
返回:
成功=1,失败=0。
说明:
param交互参数用键值对组成,每个键值对以“&”分割,如:Tsid=01&devid=002&warnid=102&warntype=01&warnlevel=1 。
测试点ID(tsid)
设备ID(devid)
告警ID(warnid)
告警类型(warntype)
告警级别(warnlevel)

代码实现
废话少说,上代码,为了演示方便,经过整理,省去了很多get,set之类的东东还有业务的东西以及Spring相关的东西。

RmiMonitorService.java
package nbpt.ts.manager.message.service;

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

/**
 * Description: 实时显示RMI服务接口.
 * 
 * RMI接口必须扩展接口java.rmi.Remote
 * 
 * @author Peter Wei
 * @version 1.0 Feb 25, 2009
 */
public interface RmiMonitorService extends Remote {
	/**
	 * 实时显示对外接口
	 * 
	 * @param funindex
	 *            功能号
	 * @param param
	 *            键名列表,也就是实际传输的内容
	 * @return
	 * @throws RemoteException
	 *             远程接口方法必须抛出java.rmi.RemoteException
	 */
	public int interactive(int funindex, String param) throws RemoteException;
}
RmiMonitorServiceImpl.java
package nbpt.ts.manager.message.service.impl;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
// import nbpt.ts.manager.base.util.AppContext;
import nbpt.ts.manager.message.service.RmiMonitorService;
import nbpt.ts.manager.message.service.WarnService;
/**
 * Description: 实时显示RMI接口实现.
 * 
 * 实现RMI接口及远程方法(继承UnicastRemoteObject)
 * 
 * @author Peter Wei
 * @version 1.0 Feb 25, 2009
 */
public class RmiMonitorServiceImpl extends UnicastRemoteObject implements
		RmiMonitorService {

	private static final long serialVersionUID = -3771656108378649574L;

	public static final int SUCCSS = 1;

	public static final int FAIL = 0;

	public WarnService warnService;

	/**
	 * 必须定义构造方法,因为要抛出RemoteException异常
	 * 
	 * @throws RemoteException
	 */
	public RmiMonitorServiceImpl() throws RemoteException {
		super();
	}

	public int interactive(int funindex, String param) throws RemoteException {

		int result = FAIL;
		switch (funindex) {
		// 告警
		case (1): {

			// warnService = (WarnService) AppContext.getAppContext().getBean(
			// "warn.warnService");
			// 实际应用是从Spring应用中获取告警Service,如上代码
			warnService = new WarnServiceImpl();
			// 网络告警的业务操作
			warnService.dealWarn(param);
			result = SUCCSS;
		}
			break;
		case (2):
			// do other biz
			break;
		}
		// ......

		return result;
	}

	public WarnService getWarnService() {
		return warnService;
	}

	public void setWarnService(WarnService warnService) {
		this.warnService = warnService;
	}

}

RmiServer.java
package nbpt.ts.manager.message.service;

import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import nbpt.ts.manager.message.service.impl.RmiMonitorServiceImpl;
/**
 * Description: RMI服务端.
 * 
 * @author Peter Wei
 * @version 1.0 Feb 25, 2009
 */
public class RmiServer {

	public String ip = "localhost";

	public int port = 8889;

	/**
	 * 启动RMI注册服务,并注册远程对象.实际应用中是在Spring初始化并启动
	 */
	public void init() {
		try {
			LocateRegistry.createRegistry(port);
			// 创建一个远程对象
			RmiMonitorService comm = new RmiMonitorServiceImpl();
			Naming.bind("//" + ip + ":" + port + "/comm", comm);
		} catch (RemoteException e) {
			System.out.println("创建远程对象发生异常!" + e.toString());
			e.printStackTrace();
		} catch (AlreadyBoundException e) {
			System.out.println("发生重复绑定对象异常!" + e.toString());
			e.printStackTrace();
		} catch (MalformedURLException e) {
			System.out.println("发生URL畸形异常!" + e.toString());
			e.printStackTrace();
		}
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public static void main(String[] args) {
		// 实际应用中是在Spring初始化并启动
		RmiServer rmiServer = new RmiServer();
		System.out.println("RMI服务初始化:");
		rmiServer.init();

	}
}
MonitorClient.java
package nbpt.ts.manager.message.service;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
/**
 * Description: RMI客户端.
 * 
 * @author Peter Wei
 * @version 1.0 Feb 25, 2009
 */
public class MonitorClient {

	public RmiMonitorService monitorService;

	public String ip = "localhost";

	public int port = 8889;

	public int interactive(int funindex, String param) {
		int result = 0;
		try {
			getMonitorService().interactive(funindex, param);
			result = 1;
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return result;
	}

	public RmiMonitorService getMonitorService() {
		try {
			// 在RMI服务注册表中查找名称为RmiMonitorService的对象,并调用其上的方法
			monitorService = (RmiMonitorService) Naming.lookup("rmi://" + ip
					+ ":" + port + "/comm");

		} catch (NotBoundException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return monitorService;
	}

	public static void main(String args[]) throws RemoteException {
		MonitorClient client = new MonitorClient();
		System.out.println("发送告警信息:");
		String msg = "tsid=1022&devid=10001027&warnid=102&warntype=01&warnlevel=1&warnmsg=设备出错,请检查.";
		System.out.println(client.getValue(msg, "warnmsg"));
		client.interactive(1, msg);

	}

	public String getValue(String content, String key) {
		String value = "";

		int begin = 0, end = 0;
		begin = content.indexOf(key + "=");
		end = content.indexOf("&", begin);

		if (end == -1)
			end = content.length();
		value = content.substring(begin + key.length() + 1, end);
		return value;

	}
}

WarnService.java
package nbpt.ts.manager.message.service;

/**
 * Description: 告警服务
 * 
 * @author Peter Wei
 * @version 1.0 2010-8-22
 */
public interface WarnService {

	/**
	 * 处理告警:告警来时的业务操作,实际操作是解析消息存库,然后界面Ajax定时刷新数据,获取实时告警展示
	 * 
	 * @param message
	 * @return
	 */
	public int dealWarn(String message);
}
WarnServiceImpl.java
package nbpt.ts.manager.message.service.impl;

import nbpt.ts.manager.message.service.WarnService;

/**
 * Description: 告警服务
 * 
 * @author Peter Wei
 * @version 1.0 2010-8-22
 */
public class WarnServiceImpl implements WarnService {

	public int dealWarn(String message) {
		// 告警处理方法
		System.out.println("已接收网络告警");
// …
		return 1;
	}

}


小结
花了4个多小时才完成本实例教程,已经半夜2点多了。大家鼓励一下吧,哈哈。

附:
引用
我也是在core java2上跑不通,yidao620c说按LZ的跑通了,请问是如何跑通的?另外,core java2没有明确的使用rmic在客户端存根,而有些帖子上讲到了这一点。
想问LZ两个问题:
1.如何运行此例。
2.rmic存根是否必要,或者说采用了其它方式代替了。
期待LZ的回复!

在Jdk5.0中,java通过动态代理和反射自动生成stub存根。我们程序中不再需要再做这一步。
   发表时间:2010-08-22  
peterwei 写道
Java分布式之RMI实例教程

Peter Wei
2010-8-22


前言
   最近的联通项目,下一阶段可能会涉及到和各省间的RMI接口,所以总结一下08年中国移动自动拨测系统用到的RMI技术,以备不时之需。同时也给广大初哥提供一些学习资料,哈哈。前几年,一直忙于项目,没怎么做系统总结。以后计划写一些以前项目用过的Java分布式技术实例教程,如:RMI、Socket、Mina、SNMP、SOAP、Web Service、Hessian、JMS等。希望和大家一起交流,分享经验,一起提高。

RMI简介
RMI,远程方法调用(Remote Method Invocation)是Enterprise JavaBeans的支柱,是建立分布式Java应用程序的方便途径。RMI是非常容易使用的,但是它非常的强大。
RMI的基础是接口,RMI构架基于一个重要的原理:定义接口和定义接口的具体实现是分开的。看看jboss-remoting:


基本原理
   要实现网络机器间的通讯,首先得来看看计算机系统网络通信的基本原理,在底层层面去看,网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络IO来实现,其中传输协议比较出名的有http、tcp、udp等等,http、tcp、udp都是在基于Socket概念上为某类应用场景而扩展出的传输协议,网络IO,主要有bio、nio、aio三种方式,所有的分布式应用通讯都基于这个原理而实现,只是为了应用的易用,各种语言通常都会提供一些更为贴近应用易用的应用层协议。

主要步骤
  分为以下几个步骤:
1. 创建远程接口及声明远程方法(RmiMonitorService.java)
2. 实现远程接口及远程方法(继承UnicastRemoteObject)(RmiMonitorServiceImpl.java)
3. 启动RMI注册服务,并注册远程对象(RmiServer.java)
4. 客户端查找远程对象,并调用远程方法(MonitorClient.java)
5. 运行实例

业务场景
在移动拨测系统管理端中要融合实时显示。简单点说就是设备出现告警时,要采用不同方式实时展示。如Web界面(Ajax)、GIS等。

   主要业务流程设计:
1. 设备告警
2. 调用RMI Client
3. 调用RMI Server
4. 调用业务处理接口
5. 告警信息入库
6. 实时显示(Ajax,Gis等技术)


技术设计
接口函数函数名称: int interactive( int funindex, string param )
参数说明:
funindex 功能号,整型。1为设备告警
param 交互参数,字符串型。
返回:
成功=1,失败=0。
说明:
param交互参数用键值对组成,每个键值对以“&”分割,如:Tsid=01&devid=002&warnid=102&warntype=01&warnlevel=1 。
测试点ID(tsid)
设备ID(devid)
告警ID(warnid)
告警类型(warntype)
告警级别(warnlevel)

代码实现
废话少说,上代码,为了演示方便,经过整理,省去了很多get,set之类的东东还有业务的东西以及Spring相关的东西。

RmiMonitorService.java
package nbpt.ts.manager.message.service;

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

/**
 * Description: 实时显示RMI服务接口.
 * 
 * RMI接口必须扩展接口java.rmi.Remote
 * 
 * @author Peter Wei
 * @version 1.0 Feb 25, 2009
 */
public interface RmiMonitorService extends Remote {
	/**
	 * 实时显示对外接口
	 * 
	 * @param funindex
	 *            功能号
	 * @param param
	 *            键名列表,也就是实际传输的内容
	 * @return
	 * @throws RemoteException
	 *             远程接口方法必须抛出java.rmi.RemoteException
	 */
	public int interactive(int funindex, String param) throws RemoteException;
}
RmiMonitorServiceImpl.java
package nbpt.ts.manager.message.service.impl;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
// import nbpt.ts.manager.base.util.AppContext;
import nbpt.ts.manager.message.service.RmiMonitorService;
import nbpt.ts.manager.message.service.WarnService;
/**
 * Description: 实时显示RMI接口实现.
 * 
 * 实现RMI接口及远程方法(继承UnicastRemoteObject)
 * 
 * @author Peter Wei
 * @version 1.0 Feb 25, 2009
 */
public class RmiMonitorServiceImpl extends UnicastRemoteObject implements
		RmiMonitorService {

	private static final long serialVersionUID = -3771656108378649574L;

	public static final int SUCCSS = 1;

	public static final int FAIL = 0;

	public WarnService warnService;

	/**
	 * 必须定义构造方法,因为要抛出RemoteException异常
	 * 
	 * @throws RemoteException
	 */
	public RmiMonitorServiceImpl() throws RemoteException {
		super();
	}

	public int interactive(int funindex, String param) throws RemoteException {

		int result = FAIL;
		switch (funindex) {
		// 告警
		case (1): {

			// warnService = (WarnService) AppContext.getAppContext().getBean(
			// "warn.warnService");
			// 实际应用是从Spring应用中获取告警Service,如上代码
			warnService = new WarnServiceImpl();
			// 网络告警的业务操作
			warnService.dealWarn(param);
			result = SUCCSS;
		}
			break;
		case (2):
			// do other biz
			break;
		}
		// ......

		return result;
	}

	public WarnService getWarnService() {
		return warnService;
	}

	public void setWarnService(WarnService warnService) {
		this.warnService = warnService;
	}

}

RmiServer.java
package nbpt.ts.manager.message.service;

import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import nbpt.ts.manager.message.service.impl.RmiMonitorServiceImpl;
/**
 * Description: RMI服务端.
 * 
 * @author Peter Wei
 * @version 1.0 Feb 25, 2009
 */
public class RmiServer {

	public String ip = "localhost";

	public int port = 8889;

	/**
	 * 启动RMI注册服务,并注册远程对象.实际应用中是在Spring初始化并启动
	 */
	public void init() {
		try {
			LocateRegistry.createRegistry(port);
			// 创建一个远程对象
			RmiMonitorService comm = new RmiMonitorServiceImpl();
			Naming.bind("//" + ip + ":" + port + "/comm", comm);
		} catch (RemoteException e) {
			System.out.println("创建远程对象发生异常!" + e.toString());
			e.printStackTrace();
		} catch (AlreadyBoundException e) {
			System.out.println("发生重复绑定对象异常!" + e.toString());
			e.printStackTrace();
		} catch (MalformedURLException e) {
			System.out.println("发生URL畸形异常!" + e.toString());
			e.printStackTrace();
		}
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public static void main(String[] args) {
		// 实际应用中是在Spring初始化并启动
		RmiServer rmiServer = new RmiServer();
		System.out.println("RMI服务初始化:");
		rmiServer.init();

	}
}
MonitorClient.java
package nbpt.ts.manager.message.service;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
/**
 * Description: RMI客户端.
 * 
 * @author Peter Wei
 * @version 1.0 Feb 25, 2009
 */
public class MonitorClient {

	public RmiMonitorService monitorService;

	public String ip = "localhost";

	public int port = 8889;

	public int interactive(int funindex, String param) {
		int result = 0;
		try {
			getMonitorService().interactive(funindex, param);
			result = 1;
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return result;
	}

	public RmiMonitorService getMonitorService() {
		try {
			// 在RMI服务注册表中查找名称为RmiMonitorService的对象,并调用其上的方法
			monitorService = (RmiMonitorService) Naming.lookup("rmi://" + ip
					+ ":" + port + "/comm");

		} catch (NotBoundException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return monitorService;
	}

	public static void main(String args[]) throws RemoteException {
		MonitorClient client = new MonitorClient();
		System.out.println("发送告警信息:");
		String msg = "tsid=1022&devid=10001027&warnid=102&warntype=01&warnlevel=1&warnmsg=设备出错,请检查.";
		System.out.println(client.getValue(msg, "warnmsg"));
		client.interactive(1, msg);

	}

	public String getValue(String content, String key) {
		String value = "";

		int begin = 0, end = 0;
		begin = content.indexOf(key + "=");
		end = content.indexOf("&", begin);

		if (end == -1)
			end = content.length();
		value = content.substring(begin + key.length() + 1, end);
		return value;

	}
}

WarnService.java
package nbpt.ts.manager.message.service;

/**
 * Description: 告警服务
 * 
 * @author Peter Wei
 * @version 1.0 2010-8-22
 */
public interface WarnService {

	/**
	 * 处理告警:告警来时的业务操作,实际操作是解析消息存库,然后界面Ajax定时刷新数据,获取实时告警展示
	 * 
	 * @param message
	 * @return
	 */
	public int dealWarn(String message);
}
WarnServiceImpl.java
package nbpt.ts.manager.message.service.impl;

import nbpt.ts.manager.message.service.WarnService;

/**
 * Description: 告警服务
 * 
 * @author Peter Wei
 * @version 1.0 2010-8-22
 */
public class WarnServiceImpl implements WarnService {

	public int dealWarn(String message) {
		// 告警处理方法
		System.out.println("已接收网络告警");
// …
		return 1;
	}

}


小结
花了4个多小时才完成本实例教程,已经半夜2点多了。大家鼓励一下吧,哈哈。

实在太简单,没有什么亮点。期待其他大作。
0 请登录后投票
   发表时间:2010-08-22  
自已以前只是做过,并没有太深入的研究,够用就好。实际的工程,更多的是业务的东西,还有其它技术的实现。写这个一方面是对以前技术的一些总结,另一方面是给初哥的入门教程。
0 请登录后投票
   发表时间:2010-08-22  
peterwei 写道
自已以前只是做过,并没有太深入的研究,够用就好。实际的工程,更多的是业务的东西,还有其它技术的实现。写这个一方面是对以前技术的一些总结,另一方面是给初哥的入门教程。

入门教程 请发到入门讨论
0 请登录后投票
   发表时间:2010-08-22  
kimmking 写道
peterwei 写道
自已以前只是做过,并没有太深入的研究,够用就好。实际的工程,更多的是业务的东西,还有其它技术的实现。写这个一方面是对以前技术的一些总结,另一方面是给初哥的入门教程。

入门教程 请发到入门讨论


JavaEye的人气就这样被打落了~~~~~~~
3 请登录后投票
   发表时间:2010-08-23  
期待后续大作
0 请登录后投票
   发表时间:2010-08-23  
rmi与其他技术相比,要暴露一个端口号,这个在正式的生产环境中,要求开放这个端口,防火墙方面需要做下设置,这个是稍微有点麻烦的地方,我们基本上是用hessian做为分布式接口技术的首选,因为是基于HTTP协议的二进制流技术,所以很容易穿透防火墙,而且hessian的对象序列化效率相当的高,还是轻量级的。
0 请登录后投票
   发表时间:2010-08-23  
同意楼上,hessian的性能和效率优于Java内置的序列化/反序列化
0 请登录后投票
   发表时间:2010-08-23  
谢谢lz的分享。
我按照《Core Java2》上面的跑不通,然后跑你的例子通了。
0 请登录后投票
   发表时间:2010-08-23   最后修改:2010-08-23
star022 写道
kimmking 写道
peterwei 写道
自已以前只是做过,并没有太深入的研究,够用就好。实际的工程,更多的是业务的东西,还有其它技术的实现。写这个一方面是对以前技术的一些总结,另一方面是给初哥的入门教程。

入门教程 请发到入门讨论


JavaEye的人气就这样被打落了~~~~~~~



je走的就不是大杂烩的路线
0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics