自定义的RPC的Java实现

来源:互联网 发布:白手起家 知乎 编辑:程序博客网 时间:2024/06/05 20:27

RPC的全名Remote Process Call,即远程过程调用。使用RPC,可以像使用本地的程序一样使用远程服务器上的程序。下面是一个简单的RPC 调用实例,从中可以看到RPC如何使用以及好处:

 

Java代码  收藏代码
  1. public class MainClient {  
  2.     public static void main(String[] args) {  
  3.         Echo echo = RPC.getProxy(Echo.class"127.0.0.1"20382);     
  4.         System.out.println(echo.echo("hello,hello"));  
  5.     }  
  6. }  
Java代码  收藏代码
  1.   

 

Java代码  收藏代码
  1. public interface Echo {  
  2.     public String echo(String string);  
  3. }  
 

 

 

 

使用RPC.getProxy生成接口Echo的代理实现类。然后就可以像使用本地的程序一样来调用Echo中的echo方法。

使用RPC的好处是简化了远程服务访问。提高了开发效率。在分发代码时,只需要将接口分发给客户端使用,在客户端看来只有接口,没有具体类实现。这样保证了代码的可扩展性和安全性。

 

在看了RPCClient如何使用,我们再来定义一个RPC服务器的接口,看看服务器都提供什么操作:

 

 

Java代码  收藏代码
  1. public interface Server {  
  2.     public void stop();  
  3.     public void start();  
  4.     public void register(Class interfaceDefiner,Class impl);  
  5.     public void call(Invocation invo);  
  6.     public boolean isRunning();  
  7.     public int getPort();  
  8. }  

 

 服务器提供了start和stop方法。使用register注册一个接口和对应的实现类。call方法用于执行Invocation指定的接口的方法名。isRunning返回了服务器的状态,getPort()则返回了服务器使用的端口。

 

来看看Invocation的定义:

 

 

Java代码  收藏代码
  1. public class Invocation implements Serializable{  
  2.     /** 
  3.      *  
  4.      */  
  5.     private static final long serialVersionUID = 1L;  
  6.       
  7.     private Class interfaces;  
  8.     private Method method;  
  9.     private Object[] params;  
  10.     private Object result;  
  11.       
  12.       
  13.     /** 
  14.      * @return the result 
  15.      */  
  16.     public Object getResult() {  
  17.         return result;  
  18.     }  
  19.     /** 
  20.      * @param result the result to set 
  21.      */  
  22.     public void setResult(Object result) {  
  23.         this.result = result;  
  24.     }  
  25.     /** 
  26.      * @return the interfaces 
  27.      */  
  28.     public Class getInterfaces() {  
  29.         return interfaces;  
  30.     }  
  31.     /** 
  32.      * @param interfaces the interfaces to set 
  33.      */  
  34.     public void setInterfaces(Class interfaces) {  
  35.         this.interfaces = interfaces;  
  36.     }  
  37.     /** 
  38.      * @return the method 
  39.      */  
  40.     public Method getMethod() {  
  41.         return method;  
  42.     }  
  43.     /** 
  44.      * @param method the method to set 
  45.      */  
  46.     public void setMethod(Method method) {  
  47.         this.method = method;  
  48.     }  
  49.     /** 
  50.      * @return the params 
  51.      */  
  52.     public Object[] getParams() {  
  53.         return params;  
  54.     }  
  55.     /** 
  56.      * @param params the params to set 
  57.      */  
  58.     public void setParams(Object[] params) {  
  59.         this.params = params;  
  60.     }  
  61.     @Override  
  62.     public String toString() {  
  63.         return interfaces.getName()+"."+method.getMethodName()+"("+Arrays.toString(params)+")";  
  64.     }  
  65.       
  66. }  
 

 

 

     具体服务器实现类中的call方法是这样使用Invocation的:

 

 

 

Java代码  收藏代码
  1. @Override  
  2.         public void call(Invocation invo) {  
  3.             Object obj = serviceEngine.get(invo.getInterfaces().getName()); //根据接口名,找到对应的处理类  
  4.             if(obj!=null) {  
  5.                 try {  
  6.                     Method m = obj.getClass().getMethod(invo.getMethod().getMethodName(), invo.getMethod().getParams());  
  7.                     Object result = m.invoke(obj, invo.getParams());  
  8.                     invo.setResult(result);  
  9.                 } catch (Throwable th) {  
  10.                     th.printStackTrace();  
  11.                 }  
  12.             } else {  
  13.                 throw new IllegalArgumentException("has no these class");  
  14.             }  
  15.         }  
 

 

  下面来看服务器接收连接并处理连接请求的核心代码:

 

 

 

Java代码  收藏代码
  1. public class Listener extends Thread {  
  2.     private ServerSocket socket;  
  3.     private Server server;  
  4.   
  5.     public Listener(Server server) {  
  6.         this.server = server;  
  7.     }  
  8.   
  9.     @Override  
  10.     public void run() {  
  11.   
  12.         System.out.println("启动服务器中,打开端口" + server.getPort());  
  13.         try {  
  14.             socket = new ServerSocket(server.getPort());  
  15.         } catch (IOException e1) {  
  16.             e1.printStackTrace();  
  17.             return;  
  18.         }  
  19.         while (server.isRunning()) {  
  20.             try {  
  21.                   
  22.                 Socket client = socket.accept();  
  23.                 ObjectInputStream ois = new ObjectInputStream(client.getInputStream());  
  24.                 Invocation invo = (Invocation) ois.readObject();  
  25.                 server.call(invo);  
  26.                 ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream());  
  27.                 oos.writeObject(invo);  
  28.                 oos.flush();  
  29.                 oos.close();  
  30.                 ois.close();  
  31.             } catch (Exception e) {  
  32.                 // TODO Auto-generated catch block  
  33.                 e.printStackTrace();  
  34.             }  
  35.   
  36.         }  
  37.   
  38.         try {  
  39.             if (socket != null && !socket.isClosed())  
  40.                 socket.close();  
  41.         } catch (IOException e) {  
  42.             // TODO Auto-generated catch block  
  43.             e.printStackTrace();  
  44.         }  
  45.     }  
  46. }  
 

 

RPC具体的Server类是这样来使用Listener的:

 

Java代码  收藏代码
  1. public static class RPCServer implements Server{  
  2.         private int port = 20382;  
  3.         private Listener listener;   
  4.         private boolean isRuning = true;  
  5.           
  6.         /** 
  7.          * @param isRuning the isRuning to set 
  8.          */  
  9.         public void setRuning(boolean isRuning) {  
  10.             this.isRuning = isRuning;  
  11.         }  
  12.   
  13.         /** 
  14.          * @return the port 
  15.          */  
  16.         public int getPort() {  
  17.             return port;  
  18.         }  
  19.   
  20.         /** 
  21.          * @param port the port to set 
  22.          */  
  23.         public void setPort(int port) {  
  24.             this.port = port;  
  25.         }  
  26.   
  27.         private Map<String ,Object> serviceEngine = new HashMap<String, Object>();  
  28.           
  29.           
  30.         @Override  
  31.         public void call(Invocation invo) {  
  32.             System.out.println(invo.getClass().getName());  
  33.             Object obj = serviceEngine.get(invo.getInterfaces().getName());  
  34.             if(obj!=null) {  
  35.                 try {  
  36.                     Method m = obj.getClass().getMethod(invo.getMethod().getMethodName(), invo.getMethod().getParams());  
  37.                     Object result = m.invoke(obj, invo.getParams());  
  38.                     invo.setResult(result);  
  39.                 } catch (Throwable th) {  
  40.                     th.printStackTrace();  
  41.                 }  
  42.             } else {  
  43.                 throw new IllegalArgumentException("has no these class");  
  44.             }  
  45.         }  
  46.   
  47.         @Override  
  48.         public void register(Class interfaceDefiner, Class impl) {  
  49.             try {  
  50.                 this.serviceEngine.put(interfaceDefiner.getName(), impl.newInstance());  
  51.                 System.out.println(serviceEngine);  
  52.             } catch (Throwable e) {  
  53.                 // TODO Auto-generated catch block  
  54.                 e.printStackTrace();  
  55.             }   
  56.         }  
  57.   
  58.         @Override  
  59.         public void start() {  
  60.             System.out.println("启动服务器");  
  61.             listener = new Listener(this);  
  62.             this.isRuning = true;  
  63.             listener.start();  
  64.         }  
  65.   
  66.         @Override  
  67.         public void stop() {  
  68.             this.setRuning(false);  
  69.         }  
  70.   
  71.         @Override  
  72.         public boolean isRunning() {  
  73.             return isRuning;  
  74.         }  
  75.           
  76.     }  
 

    服务器端代码搞定后,来看看客户端的代码,先看看我们刚开始使用RPC.getProxy方法:

 

Java代码  收藏代码
  1. public static <T> T getProxy(final Class<T> clazz,String host,int port) {  
  2.           
  3.         final Client client = new Client(host,port);  
  4.         InvocationHandler handler = new InvocationHandler() {  
  5.               
  6.             @Override  
  7.             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  8.                 Invocation invo = new Invocation();  
  9.                 invo.setInterfaces(clazz);  
  10.                 invo.setMethod(new org.jy.rpc.protocal.Method(method.getName(),method.getParameterTypes()));  
  11.                 invo.setParams(args);  
  12.                 client.invoke(invo);  
  13.                 return invo.getResult();  
  14.             }  
  15.         };  
  16.         T t = (T) Proxy.newProxyInstance(RPC.class.getClassLoader(), new Class[] {clazz}, handler);  
  17.         return t;  
  18.     }  
 

Client类的代码如下:

 

Java代码  收藏代码
  1. public class Client {  
  2.     private String host;  
  3.     private int port;  
  4.     private Socket socket;  
  5.     private ObjectOutputStream oos;  
  6.     private ObjectInputStream ois;  
  7.   
  8.     public String getHost() {  
  9.         return host;  
  10.     }  
  11.   
  12.   
  13.     public void setHost(String host) {  
  14.         this.host = host;  
  15.     }  
  16.   
  17.     public int getPort() {  
  18.         return port;  
  19.     }  
  20.     public void setPort(int port) {  
  21.         this.port = port;  
  22.     }  
  23.   
  24.     public Client(String host, int port) {  
  25.         this.host = host;  
  26.         this.port = port;  
  27.     }  
  28.   
  29.     public void init() throws UnknownHostException, IOException {  
  30.         socket = new Socket(host, port);  
  31.         oos = new ObjectOutputStream(socket.getOutputStream());  
  32.     }  
  33.   
  34.     public void invoke(Invocation invo) throws UnknownHostException, IOException, ClassNotFoundException {  
  35.         init();  
  36.         System.out.println("写入数据");  
  37.         oos.writeObject(invo);  
  38.         oos.flush();  
  39.         ois = new ObjectInputStream(socket.getInputStream());  
  40.         Invocation result = (Invocation) ois.readObject();  
  41.         invo.setResult(result.getResult());  
  42.     }  
  43.   
  44. }  
 

    至此,RPC的客户端和服务器端代码完成,启动服务器的代码如下:

 

Java代码  收藏代码
  1. public class Main {  
  2.     public static void main(String[] args) {  
  3.         Server server = new RPC.RPCServer();  
  4.         server.register(Echo.class, RemoteEcho.class);  
  5.         server.start();  
  6.     }  
  7.   
  8. }  
 

   现在先运行服务器端代码,再运行客户端代码,就可以成功运行。

   详细的代码,参考附件的源代码。

 

    在写这个RPC时,没有想太多。在数据串行化上,使用了java的标准io序列化机制,虽然不能跨平台,但是做DEMO还是不错的;另外在处理客户端请求上,使用了ServerSocket,而没有使用ServerSocketChannel这个java nio中的新特性;在动态生成接口的实现类上,使用了java.lang.reflet中的Proxy类。他可以动态创建接口的实现类。


原文链接:http://jbm3072.iteye.com/blog/1088102

 

0 0
原创粉丝点击