参拷:http://jbm3072.iteye.com/blog/1088102
目标:让客户端调用远程机器(不同JVM上)的方法.
技术:RPC(Remote Process Call远程过程调用)
优点:使用RPC,可以像使用本地的程序(本地JVM)一样使用远程服务器上的程序。使用RPC的好处是简化了远程服务访问。提高了开发效率。
做法:在分发代码时,只需要将接口分发给客户端使用,在客户端看来只有接口,没有具体类实现。这样保证了代码的可扩展性和安全性。
基础:Java反射机制,动态代理,Java IO/NIO/Socket
1:首先介绍被调用的接口与具体类
1):它在服务端与客户端都有一份
package org.jy.rpc.op; public interface MyList { void add(String s); int getSize(); }
2):具体实现,只在服务器有一份
package org.jy.rpc.op; import java.util.ArrayList; import java.util.List; public class MyListImpl implements MyList { List list=new ArrayList(); @Override public void add(String s) { list.add(s); } @Override public int getSize() { return list.size(); } }
2:rpc服务器与客户端通信用来传递参数的类
1)承载参数的类
package org.jy.rpc.protocal; import java.io.Serializable; import java.util.Arrays; /** * 用于服务器与客户端的消息传递 * @author Lenovo * */ public class Invocation implements Serializable { private static final long serialVersionUID = 1L; private Class interfaces; // 接口类 private Method method; // 方法 private Object[] params; // 参数 private Object result; // 返回值 public Object getResult() { return result; } public void setResult(Object result) { this.result = result; } public Class getInterfaces() { return interfaces; } public void setInterfaces(Class interfaces) { this.interfaces = interfaces; } public Method getMethod() { return method; } public void setMethod(Method method) { this.method = method; } public Object[] getParams() { return params; } public void setParams(Object[] params) { this.params = params; } @Override public String toString() { return interfaces.getName() + "." + method.getMethodName() + "(" + Arrays.toString(params) + ")"; } }
2):方法类(我认为可以不用它)
package org.jy.rpc.protocal; import java.io.Serializable; /** * 用来存放方法名及参数类型 * @author Lenovo */ public class Method implements Serializable{ private static final long serialVersionUID = 1L; private String methodName; //方法名称 private Class[] params; //参数类型 public Method(String name, Class<?>[] parameterTypes) { this.methodName = name; this.params = parameterTypes; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public Class[] getParams() { return params; } public void setParams(Class[] params) { this.params = params; } }
3:服务器端类
1)服务器入口
package org.jy.rpc; import org.jy.rpc.op.MyList; import org.jy.rpc.op.MyListImpl; import org.jy.rpc.support.RPCServer; import org.jy.rpc.support.Server; public class Main { public static void main(String[] args) { // 创建服务器 Server server = new RPCServer(); // 注册可被调用的类 server.register(MyList.class, MyListImpl.class); // 启动服务监听 server.start(); } }
2)服务类接口
package org.jy.rpc.support; import org.jy.rpc.protocal.Invocation; public interface Server { public void stop(); public void start(); public void register(Class interfaceDefiner,Class impl); //注册接口与具体实现 public void call(Invocation invo); //方法调用 public boolean isRunning(); public int getPort(); }
3)服务类(主要就是跟据传来的接口名找到具体实例,利用反射调用方法返回结果)
package org.jy.rpc.support; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import org.jy.rpc.protocal.Invocation; public class RPCServer implements Server { private int port = 20382; private Listener listener; private boolean isRuning = true; public void setRuning(boolean isRuning) { this.isRuning = isRuning; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } private Map<String, Object> serviceEngine = new HashMap<String, Object>(); // 存放接口名称与具体实例 @Override public void call(Invocation invo) { Object obj = serviceEngine.get(invo.getInterfaces().getName());// 得到具体实现 if (obj != null) { try {// 类存在反射方法 Method m = obj.getClass().getMethod( invo.getMethod().getMethodName(), invo.getMethod().getParams()); Object result = m.invoke(obj, invo.getParams()); invo.setResult(result); } catch (Throwable th) { th.printStackTrace(); } } else { throw new IllegalArgumentException("has no these class"); } } @Override public void register(Class interfaceDefiner, Class impl) { try { this.serviceEngine.put(interfaceDefiner.getName(), impl.newInstance()); } catch (Throwable e) { e.printStackTrace(); } } @Override public void start() { System.out.println("启动服务器"); listener = new Listener(this); this.isRuning = true; listener.start(); } @Override public void stop() { this.setRuning(false); } @Override public boolean isRunning() { return isRuning; } }
4):监听类(主要用来开启socker监听,接收返回Invocation )
package org.jy.rpc.support; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; import org.jy.rpc.protocal.Invocation; public class Listener extends Thread { private ServerSocket socket; private Server server; public Listener(Server server) { this.server = server; } @Override public void run() { System.out.println("启动服务器中,打开端口" + server.getPort()); try { socket = new ServerSocket(server.getPort()); } catch (IOException e1) { e1.printStackTrace(); return; } while (server.isRunning()) { try { System.out.println("等待请求"); Socket client = socket.accept(); System.out.println("请求到来"); ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); Invocation invo = (Invocation) ois.readObject(); System.out.println("远程调用:" + invo); server.call(invo); ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream()); oos.writeObject(invo); oos.flush(); oos.close(); ois.close(); } catch (Exception e) { e.printStackTrace(); } } try { if (socket != null && !socket.isClosed()) socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
4:客户端
1)入口类
package org.jy.rpc; import org.jy.rpc.op.MyList; public class MainClient { public static void main(String[] args) { MyList proxy = RPC.getProxy(MyList.class, "127.0.0.1", 20382); proxy.add("gbz"); System.out.println(proxy.getSize()); } }
2):客户端的数据传送类(就是连接远程的socket,传参数,等返回)
package org.jy.rpc.support; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; import java.net.UnknownHostException; import org.jy.rpc.protocal.Invocation; public class Client { private String host; private int port; private Socket socket; private ObjectOutputStream oos; private ObjectInputStream ois; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public Client(String host, int port) { this.host = host; this.port = port; } public void init() throws UnknownHostException, IOException { socket = new Socket(host, port); oos = new ObjectOutputStream(socket.getOutputStream()); } public void invoke(Invocation invo) throws UnknownHostException, IOException, ClassNotFoundException { init(); System.out.println("写入数据"); oos.writeObject(invo); oos.flush(); ois = new ObjectInputStream(socket.getInputStream()); Invocation result = (Invocation) ois.readObject(); invo.setResult(result.getResult()); } }
3):动态代理(产生具体类的代理类,调用代理类方法时执行invoke方法,它会通过客户端的socket调用服务器的具体实例的方法)
package org.jy.rpc; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.jy.rpc.protocal.Invocation; import org.jy.rpc.support.Client; public class RPC { public static <T> T getProxy(final Class<T> clazz,String host,int port) { final Client client = new Client(host,port); InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Invocation invo = new Invocation(); invo.setInterfaces(clazz); invo.setMethod(new org.jy.rpc.protocal.Method(method.getName(),method.getParameterTypes())); invo.setParams(args); client.invoke(invo); //调用远程服务器的方法 return invo.getResult(); } }; T t = (T) Proxy.newProxyInstance(RPC.class.getClassLoader(), new Class[] {clazz}, handler); return t; } }