RMI(即Remote Method Invoke 远程方法调用)
-
远程对象: 用于远程客户端调用 必需继承java.rmi.Remote,每个调用方法必须添加java.rmi.RemoteException异常
-
远程对象实现:用于远程服务器,实现调用逻辑 必需继承UnicastRemoteObject
-
远程服务器:通过LocateRegistry注册服务
-
远程客户端:通过LocateRegistry查找服务,调用方法即可发送内容 Naming是LocateRegistry的辅助工具类
import java.rmi.Remote; import java.rmi.RemoteException; public interface ITestRMIService extends Remote { //必须加RemoteException否则运行出错: java.rmi.server.ExportException public String a(String content) throws RemoteException; public TestRMIObj b(TestRMIObj input) throws RemoteException; }
// UnicastRemoteObject用于导出的远程对象和获得与该远程对象通信的存根。否则运行出错:java.rmi.MarshalException public class TestRMIServiceImpl extends UnicastRemoteObject implements ITestRMIService { private static final long serialVersionUID = -6460154344452562895L; public TestRMIServiceImpl() throws RemoteException { super(); } @Override public String a(String content) { System.out.println("call : a"+ content ); return "server >> " + content; } @Override public TestRMIObj b(TestRMIObj input) throws RemoteException { System.out.println("call b : "+ input.getB().length ); input.setA(12); return input; } }
//必须实现Serializable该对象要支持 Serializable public class TestRMIObj implements Serializable { private static final long serialVersionUID = 2367853017282270281L; private int a; private Integer[] b; public int getA() { return a; } public Integer[] getB() { return b; } public void setA(int a) { this.a = a; } public static TestRMIObj of(int a, Integer... b) { TestRMIObj ret = new TestRMIObj(); ret.a = a; ret.b = b; return ret; } }
public class ServerRMI { public static void main(String[] args) { try { // 实例化实现了IService接口的远程服务ServiceImpl对象 ITestRMIService service02 = new TestRMIServiceImpl(); // 本地主机上的远程对象注册表Registry的实例,并指定端口为8888 LocateRegistry.createRegistry(8888); // 把远程对象注册到RMI注册服务器上,并命名为service02 // 绑定的URL标准格式为:rmi://host:port/name Naming.bind("rmi://localhost:8888/service02", service02); } catch (Exception e) { e.printStackTrace(); } System.out.println("服务器向命名表注册了1个远程服务对象!"); } }
public class ClientRMI { private ITestRMIService service; public void start() { String url = "rmi://localhost:8888/"; try { // 在RMI服务注册表中查找名称为service02的对象,并调用其上的方法 service = (ITestRMIService) Naming.lookup(url + "service02"); System.out.println(service.a("你好!")); System.out.println(service.b(TestRMIObj.of(1, 2, 3, 4)).getA()); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception { ClientRMI rmi = new ClientRMI(); rmi.start(); while (true) { Thread.sleep(500); rmi.service.a("a"); } } }
使用rmi开发网络通信非常简单,代码量比写model还少,开发者不需要考虑对象序列化,网络IO模型,粘包/半包、传输协议等问题
但它应用场景非常少,不适合大规模的应用,比较适合s/s服务间调用,原因是对象序列化数据量大,采用的是BIO模型
RMI与RPC有什么区别呢
RPC(Remote Procedure Call Protocol)远程过程调用协议
程序可使用这种协议向网络中的另一台计算机上的程序请求服务。由于使用 RPC 的程序不必了解支持通信的网络协议的情况,因此 RPC 提高了程序的互操作性。在 RPC 中,发出请求的程序是客户程序,而提供服务的程序是服务器
可以这样理解rmi是sun公司基于rpc制定的实现标准