Java RMI详解
一丶RMI 定义
RMI(Remote Method Invocation):远程方法调用。能够让某个 java 虚拟机上的对象调用运行在另一个 java 虚拟机上的对象的方法,就像调用本地方法一样。这两个虚拟机可以是运行在相同计算机上不同组件的不同进程中,也可以是运行在网络上不同计算机的当中,解决系统之间的通信问题。
二丶RMI调用时序图
三丶关于 RMI 的例子
客户端接口:
public interface HelloRmi extends Remote { public String sayHello() throws RemoteException; }
接口实现:
public class HelloRmiImpl extends UnicastRemoteObject implements HelloRmi,Serializable { public HelloRmiImpl() throws RemoteException { } public String sayHello() throws RemoteException { return "Hello Rmi......"; } }
发布服务:
public class RmiServer { public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException { HelloRmi rmi = new HelloRmiImpl(); String url = publishService(rmi, "127.0.0.1", 1099); System.out.println("rmi service has bean started......"); } // 发布 RMI 服务 private static String publishService(Remote remote, String host, int port) { String url = null; try { /** * host 是注册表所在的主机(远程或本地) * port 是注册表接受调用的端口号,默认为 1099 */ url = String.format("rmi://%s:%d/%s", host, port, remote.getClass().getName()); //创建并导出接受指定 port 请求的本地主机上的 Registry 实例 LocateRegistry.createRegistry(port); // 将指定的名称 url 重新绑定到一个新的远程对象 Naming.rebind(url, remote); System.out.println("publish rmi service,url: = " + url); } catch (RemoteException e) { e.printStackTrace(); }catch (MalformedURLException e) { e.printStackTrace(); } return url; } }
调用服务:
public class RmiClient { public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException { String url= "rmi://127.0.0.1:1099/com.lai.rmi.server.HelloRmiImpl"; //返回与指定名称 url 关联的远程对象的引用 HelloRmi rmi = (HelloRmi) Naming.lookup(url); System.out.println(rmi.sayHello()); } }
四丶注意的细节
1丶因为方法的参数跟返回值最终都是会在网络上传输,所以实现必须是可序列化的
2丶UnicastRemoteObject类的构造函数抛出了RemoteException,故其继承类不能使用默认构造函数,继承类的构造函数必须也抛出RemoteException
3丶Registry 是简单远程对象注册表的一个远程接口,它提供存储和获取绑定了任意字符串名称的远程对象引用的方法
4丶Remote 接口用于标识其方法可以从非本地虚拟机上调用的接口
5丶Skeleton(代理,学术名骨架) Stub(代理):学术名存根。在服务端我们发布了RMI服务,并在JNDI中进行了注册,此时就在服务端创建了一个Skeleton,当客户端第一次成功连接JNDI并获取远程服务对象后,立马在本地创建了一个Stub
6丶远程通信实际是通过Skeleton与Stub来完成的,数据是基于TCP/IP协议
在服务端我们发布了RMI服务,并在JNDI中进行了注册,此时就在服务端创建了一个Skeleton(骨架),当客户端第一次成功连接JNDI并获取远程服务对象后,立马在本地创建了一个Stub(存根)