从零手写RPC

公司很多同事对RPC不是很理解,所以手写了一个rpc框架,顺便把原理给他们讲了下,特意发在博客上,希望对大家有帮助。

1、什么是rpc

rpc即远程过程调用,是Remote Procedure Call,说人话,就是在本地调用其他系统提供的方法,就像调用本地方法一样。

2、实现原理

有通讯,就少不了IO

有对象通过网络传输,就少不了序列化

本地是不可能直接调用远程方法的,所以需要代理

用到了代理,怎么可能缺了他--反射

说人话:rpc就是客户端把方法和参数对象序列化后通过IO通讯传给服务器端,服务器收到后,通过反射机制,调用服务器方法,把生成的结果对象序列化后通过IO返回给客户端。

直接上图:

3、代码实现

接口类

package rpc.max;

public interface HelloService {

    String hello(String name);
}
 

实现类

package rpc.max;

public class HelloServiceImpl implements HelloService {

    public String hello(String name) {
        return "Hello " + name;
    }

}

rpc工具类

package rpc.max;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class RpcUtil {
    
    private static ExecutorService threadTool = Executors.newFixedThreadPool(5);

    public static void publish(final Object service, int port) throws Exception {

        ServerSocket serverSocket = new ServerSocket(port);
        while (true) {
            final Socket socket = serverSocket.accept();

            threadTool.execute(new Runnable() {

                public void run() {
                    ObjectInputStream ois = null;
                    try {
                        ois = new ObjectInputStream(socket.getInputStream());

                        try {
                            // 拿到入参
                            String methodName = ois.readUTF();
                            Class<?>[] parameterTypes = (Class<?>[]) ois.readObject();
                            Object[] arguments = (Object[]) ois.readObject();

                            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

                            try {
                                // 反射调用
                                Method method = service.getClass().getMethod(methodName, parameterTypes);
                                Object result = method.invoke(service, arguments);
                                oos.writeObject(result);
                            } catch (Exception e) {
                                oos.writeObject(e);
                            } finally {
                                oos.close();
                            }

                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }

                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (null != ois) {
                            try {
                                ois.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                        try {
                            socket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
        }
    }

    public static <T> T Proxy(Class<T> interfaces, final String host, final int port) {
        Object instance = Proxy.newProxyInstance(interfaces.getClassLoader(), new Class<?>[] { interfaces },
                new InvocationHandler() {

                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Socket socket = new Socket(host, port);
                        try {
                            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                            oos.writeUTF(method.getName());
                            oos.writeObject(method.getParameterTypes());
                            oos.writeObject(args);
                            ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                            try {
                                Object result = ois.readObject();
                                if (result instanceof Throwable) {
                                    throw (Throwable) result;
                                }

                                return result;

                            } finally {
                                ois.close();
                                oos.close();
                            }
                        } finally {
                            socket.close();
                        }
                    }
                });

        return (T) instance;
    }

}

RPC框架

    /**
     * 引用服务
     * 
     * @author max.zhang
     * @since 2018年9月17日
     * @param interfaceName
     * @param host
     * @param port
     * @return
     * @throws Exception
     */
    public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {
        if (null == interfaceClass) {
            throw new Exception("interfaceClass can not be null");
        }
        if (!interfaceClass.isInterface()) {
            throw new Exception(interfaceClass.getName() + " is not interface");
        }
        if (null == host || host.length() == 0) {
            throw new Exception("host is null");
        }
        if (port < 0 || port > 65535) {
            throw new Exception("invalid port");
        }

        System.out.println("Get remote service " + interfaceClass.getName() + "from: " + host + " " + port);

        // 根据传入的接口生成动态代理类
        Object instance = Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] { interfaceClass },
                new InvocationHandler() {

                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Socket socket = new Socket(host, port);
                        try {
                            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                            // 两边参数的读写必须匹配
                            oos.writeUTF(method.getName());
                            oos.writeObject(method.getParameterTypes());
                            oos.writeObject(args);

                            // 一般的动态代理代码中,我们会在invoke里调用真实对象。
                            // 通过socket传递参数去执行,拿到结果
                            // 这就是一个简单的rpc实现
                            ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                            try {
                                Object result = ois.readObject();
                                if (result instanceof Throwable) {
                                    throw (Throwable) result;
                                }

                                return result;

                            } finally {
                                ois.close();
                                oos.close();
                            }
                        } finally {
                            socket.close();
                        }
                    }
                });

        return (T) instance;

    }
}

提供者

package rpc.max;

public class RpcProvider {

    public static void main(String[] args) throws Exception {
        HelloService hs = new HelloServiceImpl();
        RpcUtil.publish(hs, 9200);
    }
}

消费者

public class RpcConsumer {

    public static void main(String[] args) {
        HelloService hs = RpcUtil.Proxy(HelloService.class, "127.0.0.1", 9200);
        System.out.println(hs.hello("max"));
    }
}

亲测可用,转走不谢

猜你喜欢

转载自blog.csdn.net/hczjb/article/details/82740287