自定义的RPC的Java实现

        网上看到纯java实现的RPC,很不错。

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

package com.bijian.rpc;

import com.bijian.rpc.op.Echo;

public class MainClient {
    
    public static void main(String[] args) {
        
        Echo echo = RPC.getProxy(Echo.class, "127.0.0.1", 8080);

        System.out.println(echo.echo("hello,hello"));
        System.out.println(echo.echo("hellow,rod"));
        System.out.println(echo.echo("hellow,bijian"));
        System.out.println(echo.echo("hellow,rod"));
        System.out.println(echo.echo("hellow,rod"));
        System.out.println(echo.echo("hellow,rod"));
    }
}
package com.bijian.rpc.op;

public interface Echo {
    
    public String echo(String string);
}

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

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

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

package com.bijian.rpc.support;

import com.bijian.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();
}

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

        来看看Invocation的定义:

package com.bijian.rpc.protocal;

import java.io.Serializable;
import java.util.Arrays;

public class Invocation implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private Class interfaces;
    private Method method;
    private Object[] params;
    private Object result;

    /**
     * @return the result
     */
    public Object getResult() {
        return result;
    }

    /**
     * @param result the result to set
     */
    public void setResult(Object result) {
        this.result = result;
    }

    /**
     * @return the interfaces
     */
    public Class getInterfaces() {
        return interfaces;
    }

    /**
     * @param interfaces the interfaces to set
     */
    public void setInterfaces(Class interfaces) {
        this.interfaces = interfaces;
    }

    /**
     * @return the method
     */
    public Method getMethod() {
        return method;
    }

    /**
     * @param method the method to set
     */
    public void setMethod(Method method) {
        this.method = method;
    }

    /**
     * @return the params
     */
    public Object[] getParams() {
        return params;
    }

    /**
     * @param params the params to set
     */
    public void setParams(Object[] params) {
        this.params = params;
    }

    @Override
    public String toString() {
        return interfaces.getName() + "." + method.getMethodName() + "(" + Arrays.toString(params) + ")";
    }
}

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

package com.bijian.rpc;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import com.bijian.rpc.protocal.Invocation;
import com.bijian.rpc.support.Listener;
import com.bijian.rpc.support.Server;

public class RPC {
    
    public static class RPCServer implements Server {
        private int port = 8080;
        private Listener listener;
        private boolean isRuning = true;

        /**
         * @param isRuning the isRuning to set
         */
        public void setRuning(boolean isRuning) {
            this.isRuning = isRuning;
        }

        /**
         * @return the port
         */
        public int getPort() {
            return port;
        }

        /**
         * @param port the port to set
         */
        public void setPort(int port) {
            this.port = port;
        }

        private Map<String, Object> serviceEngine = new HashMap<String, Object>();

        @Override
        public void call(Invocation invo) {
            System.out.println(invo.getClass().getName());
            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());
                System.out.println(serviceEngine);
            } catch (Throwable e) {
                // TODO Auto-generated catch block
                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;
        }
    }
}

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

package com.bijian.rpc.support;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

import com.bijian.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) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        try {
            if (socket != null && !socket.isClosed())
                socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

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

package com.bijian.rpc;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.bijian.rpc.protocal.Invocation;
import com.bijian.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 com.bijian.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;
    }
}

        Client类的代码如下:

package com.bijian.rpc.support;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

import com.bijian.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());
    }
}

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

package com.bijian.rpc;

import com.bijian.rpc.op.Echo;
import com.bijian.rpc.op.RemoteEcho;
import com.bijian.rpc.support.Server;

public class Main {
    
    public static void main(String[] args) {
        Server server = new RPC.RPCServer();
        server.register(Echo.class, RemoteEcho.class);
        server.start();
    }
}

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

        Client运行输出:

写入数据
服务器回应:hello,hello
写入数据
服务器回应:hellow,rod
写入数据
服务器回应:hellow,bijian
写入数据
服务器回应:hellow,rod
写入数据
服务器回应:hellow,rod
写入数据
服务器回应:hellow,rod

        Server运行输出:

{com.bijian.rpc.op.Echo=com.bijian.rpc.op.RemoteEcho@1fb8ee3}
开始启动服务
启动服务器中,打开端口8080
等待请求
请求到来
远程调用:com.bijian.rpc.op.Echo.echo([hello,hello])
com.bijian.rpc.protocal.Invocation
等待请求
请求到来
远程调用:com.bijian.rpc.op.Echo.echo([hellow,rod])
com.bijian.rpc.protocal.Invocation
等待请求
请求到来
远程调用:com.bijian.rpc.op.Echo.echo([hellow,bijian])
com.bijian.rpc.protocal.Invocation
等待请求
请求到来
远程调用:com.bijian.rpc.op.Echo.echo([hellow,rod])
com.bijian.rpc.protocal.Invocation
等待请求
请求到来
远程调用:com.bijian.rpc.op.Echo.echo([hellow,rod])
com.bijian.rpc.protocal.Invocation
等待请求
请求到来
远程调用:com.bijian.rpc.op.Echo.echo([hellow,rod])
com.bijian.rpc.protocal.Invocation
等待请求

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

 

文章来源:http://jbm3072.iteye.com/blog/1088102

你可能感兴趣的:(java,rpc)