本文共 7953 字,大约阅读时间需要 26 分钟。
RMI
RMI——Remote Method Invocation,即远程方法调用。是Java的一组拥护开发分布式应用程序的API。。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。
RMI主要用到了代理模式和序列化/反序列化。
一个RMI系统的组成元素:
1. 远程服务接口定义
2. 远程服务接口实现
3. 远程服务框架代理类(Skeleton)
4. 客户端桩代理类(Stub)
5. 客户端
6. 一个RMI命名服务或注册中心,允许服务端注册服务,客户端发现服务
基本原理:
RMI系统的运行过程:
1. 生成一个远程接口
2. 实现远程接口(服务端程序)
3. 注册远程服务到注册中心
4. 启动服务端,等待客户端调用
5. 编写客户端程序
6. 客户端去注册中心获取服务
7. 客户端调用
8. 服务端相应,处理服务,返回相应
9. 客户端拿到响应,一次交互结束。
时序图:
Server接口:
public interface MyRemote extends Remote { public String sayHello() throws RemoteException; } |
ServerImpl:服务端实现类:
public class MyRemoteImpl implements MyRemote, Serializable { public String sayHello() throws RemoteException { return "server say Hello!"; } public static void main(String args[]) { MyRemote myRemote = new MyRemoteImpl(); try { Registry registry = LocateRegistry.createRegistry(2001); //开一个端口 registry.rebind("RemoteHello", myRemote); //服务端注册 System.out.println("server is ready"); try { Thread.sleep(1000 * 20); //由于是在一个工程内,同时runmain方法,这里sleep一下,保证客户端在20秒内调到 } catch (InterruptedException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } } catch (RemoteException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } } } |
Client:客户端类
public class Client { public static void main(String args[]) { try { Registry registry = LocateRegistry.getRegistry("127.0.0.1", 2001); MyRemote myRemote = (MyRemote) registry.lookup("RemoteHello"); //从注册中心取到服务 String s = myRemote.sayHello();//客户端请求服务 System.out.println("s=" + s); } catch (NotBoundException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } catch (RemoteException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } } } |
Server:
package com.mylearn.rmi.principle.remote; /** * Created by IntelliJ IDEA. * User: yingkuohao * Date: 13-10-30 * Time: 下午5:21 * CopyRight:360buy * Descrption: 声明接口,客户端和服务端都要知道 * To change this template use File | Settings | File Templates. */ public interface Server { /** * 提供服务一: sayHello * * @return */ public String sayHello(); /** * 提供服务二: sayLove * * @return */ public String sayLove(); } |
ServerImpl:
package com.mylearn.rmi.principle.remote; import java.io.Serializable; /** * Created by IntelliJ IDEA. * User: yingkuohao * Date: 13-10-30 * Time: 下午5:24 * CopyRight:360buy * Descrption: 服务端具体实现类,注意要实现Serilizable接口 * To change this template use File | Settings | File Templates. */ public class ServerImpl implements Server, Serializable { public String sayHello() { return "server say hello!"; //To change body of implemented methods use File | Settings | File Templates. } public String sayLove() { return "server say love!"; //To change body of implemented methods use File | Settings | File Templates. } } |
Server_Skeleton :服务端代理类
package com.mylearn.rmi.principle.remote; import javax.print.attribute.standard.Severity; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * Created by IntelliJ IDEA. * User: yingkuohao * Date: 13-10-30 * Time: 下午5:23 * CopyRight:360buy * Descrption: 服务端代理类,代理类的作用就是实现类的封闭, * 1. 这里接收客户端的字节流,然后解析出客户端要请求的服务, * 2.然后找到服务的本尊,调用对应的方法,得出结果 * 3. 然后把结果序列化,通过流的方式返回给客户端(其实也是客户端的代理对象) * To change this template use File | Settings | File Templates. */ public class Server_Skeleton implements Runnable { private Server server; //声明代理对象 public void run() { try { ServerSocket serverSocket = new ServerSocket(9090); //开启socket服务端 while (true) { Socket socket = serverSocket.accept(); //等待客户端请求 System.out.println("服务端建立socket链接ok!"); InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); String method = (String) objectInputStream.readObject(); //获取客户端请求方法,反序列化 if ("sayHello".equalsIgnoreCase(method)) { //如果是saylHello方法,则调用ServerImpl去处理,代理模式 server = new ServerImpl(); String s = server.sayHello(); objectOutputStream.writeObject(s); //序列化 objectOutputStream.flush(); } if ("sayLove".equalsIgnoreCase(method)) { //如果是sayLove方法,则调用ServerImpl去处理 server = new ServerImpl(); String s = server.sayLove(); objectOutputStream.writeObject(s); objectOutputStream.flush(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } outputStream.close(); inputStream.close(); //输入流不能提前关闭,否则报错 socket.close(); System.out.println("服务端响应结束"); } } catch (IOException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } catch (ClassNotFoundException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } } } |
Client_Stub:客户端代理类
package com.mylearn.rmi.principle.local; import com.mylearn.rmi.principle.remote.Server; import java.io.*; import java.net.Socket; /** * Created by IntelliJ IDEA. * User: yingkuohao * Date: 13-10-30 * Time: 下午5:24 * CopyRight:360buy * Descrption: 客户端代理类 * 1. 把客户端的请求通过流传给服务端(其实是服务端的代理类) * 2. 阻塞,等待服务端的返回,如果有响应,则读取响应信息,反序列化,展现结果 * To change this template use File | Settings | File Templates. */ public class Client_Stub implements Server { private Socket socket; private void connect() { try { socket = new Socket("127.0.0.1", 9090); } catch (IOException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } } public String sayHello() { return say("sayHello"); } public String sayLove() { return say("sayLove"); } public String say(String fieldName) { connect();//建立到服务端的链接 try { InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(fieldName); //向服务端发送请求,序列化,请求调用服务端的sayHello方法 // System.out.println("客户端发送请求,request sayHello接口"); objectOutputStream.flush(); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); return (String) objectInputStream.readObject(); //阻塞,等待服务端发送消息,反序列化 } catch (IOException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } catch (ClassNotFoundException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } return null; } } |
Client:
package com.mylearn.rmi.principle.local; import com.mylearn.rmi.principle.remote.Server_Skeleton; /** * Created by IntelliJ IDEA. * User: yingkuohao * Date: 13-10-30 * Time: 下午5:21 * CopyRight:360buy * Descrption: 客户端对象 * To change this template use File | Settings | File Templates. */ public class Client { public static void main(String args[]) { //1.开一个服务器线程 final Thread threadServer1 = new Thread(new Server_Skeleton()); //2.开5个客户端线程请求sayHello for (int i = 0; i < 5; i++) { Thread threadClient1 = new Thread(new Runnable() { public void run() { Client_Stub client_stub = new Client_Stub(); String response = client_stub.sayHello(); System.out.println("response1 = " + response); } }); threadClient1.start(); } //3.开5个客户端线程请求sayLove for (int i = 0; i < 5; i++) { Thread threadClient2 = new Thread(new Runnable() { public void run() { Client_Stub client_stub = new Client_Stub(); String response = client_stub.sayLove(); System.out.println("response2 = " + response); } }); threadClient2.start(); } threadServer1.start();//启动服务端代理类线程 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } } } |
五、设计模式延伸
代理模式: 代理模式控制对象的访问。在代理类中会对客户端的访问做限制,达到某个条件才会调用对象本尊。如上文中的Server_Skeleton和Client_Stub对象都是代理类。ServerImpl是服务本尊。
装饰模式:装饰模式为对象增加行为。
适配器模式:代理和适配器都是会挡在其他对象的前面,并负责将请求转发给它们。适配器会改变对象适配的接口,而代理则实现相同的接口。
推荐: