1 分布式计算原理:
a 服务器提供接口做成.jar文件给客户端,监听客户端。
b 客户端通过服务器提供的接口,调用方法,发送请求给服务器。
c 服务器根据请求调用接口实现的方法,并通过Socket将结果给客户端。
如图:
2 服务端需要.jar:
rt.jar
拥有必要的类等:
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
3 如何创建远程服务
a.创建Remote接口
远程的接口定义了客户端可以远程调用的方法,它是个作为服务的多态化类.stub和服务都会实现此接口
b.实现Remote接口
这个是真正执行的类,它实现出定义在该接口上的方法,它是客户端会调用的对象
c.用rmic产生stub和skeleton
客户端和服务器都有helper,我们无需创建这些类或产生这些类的源代码,这都会在执行JDK所附的rmic工具时自动地处理掉
d.启动RMI registry (rmiregistry)
rmiregistry就像电话薄,用户会从此处取得代理(客户端的stub/helper对象)
e.启动远程服务
必须让服务对象开始执行,实现服务的类会起始服务的实例并向RMI Registry注册,要有注册后才能对用户服务
4 代码演示:
-------------------------------------------------------
客户端接口:
public interface MyRemote extends Remote
{
/**
* 远程的接口定义了客户端可以远程调用的方法,它是作为服务的多态化类,也就是说,客户端会
* 调动有实现此接口的stub,而此stub因为会执行网络和输入/输出工作,所以可能会发生各种
* 问题,客户端必须处理或声明异常来认知这一类风险,如果该方法在接口中声明异常,调用该方
* 法的所有程序都必须处理或再声明此异常.
*
* 远程方法的参数和返回值必须是primitive或serializable的.任何远程方法的参数都会被
* 打包通过网络传送,而这时通过序列化完成的,返回值也是一样.所以,如果使用的是自定义类型
* 时,必须对其序列化
* @return
* @throws RemoteException
* 所有接口中的方法都必须声明RemoteException
*/
public String sayHello() throws RemoteException;
public List<String> getList() throws RemoteException;
}
注意:将其打成jar包。
-------------------------------------------------------
服务器端接口实现:
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote
{
/**
* 注释内容
*/
private static final long serialVersionUID = 1L;
/**
* 父类的构造函数声明了异常,所有你必须写出构造函数,因为它代表你的构造函数会调用有风险的程序代码
*
* UnicastRemoteObject有个小问题,它的构造函数会抛出RemoteException.处理它的唯一方式就是
* 对自己的实现声明一个构造,如此才会有地方可以声明出RemoteException.当类被初始化的时候,父类
* 的构造函数一定会被调用,如果父类的构造函数抛出异常,我们也必须声明的自定义的构造函数会抛出异常
* @throws RemoteException
*/
protected MyRemoteImpl()
throws RemoteException
{
super();
}
/** {@inheritDoc} */
@Override
public String sayHello() throws RemoteException
{
return "hello! wangming";
}
@Override
public List<String> getList() throws RemoteException
{
List<String> list = new ArrayList<String>();
list.add("cat");
list.add("dog");
list.add("tiger");
list.add("bird");
return list;
}
}
-------------------------------------------------------
服务器端等待客户调用:
public class ProductServer
{
public static void main(String[] args)
{
try
{
LocateRegistry.createRegistry(8808);
MyRemote service = new MyRemoteImpl();
//172.17.206.5
Naming.rebind("//172.17.206.5:8808/SAMPLE-SERVER", service);
System.out.println("远程对象注册成功, RMI 服务已经启动,等待客户端调用 ....");
}
catch (java.net.MalformedURLException me)
{
System.out.println("Malformed URL:" + me.toString());
}
catch (RemoteException re)
{
System.out.println("Remote exception:" + re.toString());
}
}
}
-------------------------------------------------------
客户端调用接口方法:
public class MyRemoteClient
{
public static void main(String[] args)
{
try
{
String url = "//172.17.206.5:8808/SAMPLE-SERVER";
MyRemote product = (MyRemote)Naming.lookup(url);
System.out.println(product.sayHello());
List<String> list = product.getList();
for(String l:list)
{
System.out.println(l);
}
}
catch (RemoteException exc)
{
System.out.println("Error in lookup: " + exc.toString());
}
catch (java.net.MalformedURLException exc)
{
System.out.println("Malformed URL: " + exc.toString());
}
catch (java.rmi.NotBoundException exc)
{
System.out.println("NotBound: " + exc.toString());
}
}
}