一个简易RPC调用

燃着的半支烟 2020年04月13日 50次浏览

一个简易的RPC调用,主要用到socket、多线程、动态代理、Java反射。

RpcServer

RpcServer主要用来暴露服务。

public void export(Object service, int port) throws Exception {
        if (service == null) {
            throw new IllegalArgumentException("service instance == null");
        }
        if (port <= 0 || port > 65535) {
            throw new IllegalArgumentException("Invalid post " + port);
        }

        ServerSocket server = new ServerSocket(port);
        while (true) {
            final Socket socket = server.accept();
            new Thread(() -> {
                try {
                    ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());

                    try {
                        String methodName = input.readUTF();//IO堵塞,获取方法名
                        Class<?>[] parameterTypes = (Class<?>[]) input.readObject();//IO堵塞,获取参数类型
                        Object[] arguments = (Object[]) input.readObject();//IO堵塞,取参数

                        //执行接口方法
                        Method method = service.getClass().getMethod(methodName, parameterTypes);
                        Object result = method.invoke(service, arguments);

                        //将结果写出去
                        output.writeObject(result);
                    } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                    } finally {
                        output.close();
                        input.close();
                        socket.close();
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }

    }

RpcClient

RpcClient主要用来引用远程服务。

public <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {
        if (interfaceClass == null) {
            throw new IllegalArgumentException("Interface class == null");
        }
        if (!interfaceClass.isInterface()) {
            throw new IllegalArgumentException("the " + interfaceClass.getName() + " must be interface class!");
        }
        if (host == null || host.length() == 0) {
            throw new IllegalArgumentException("Host == null!");
        }
        if (port <= 0 || port > 65535) {
            throw new IllegalArgumentException("Invalid port " + port);
        }

        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[]{interfaceClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket(host, port);
                try {
                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                    try {
                        output.writeUTF(method.getName());//写出方法名
                        output.writeObject(method.getParameterTypes());//写出参数类型
                        output.writeObject(args);//写出参数

                        //IO堵塞,等待获取执行结果
                        ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                        try {
                            Object result = input.readObject();
                            if (result instanceof Throwable) {
                                throw (Throwable) result;
                            }
                            return result;
                        } finally {
                            input.close();
                        }
                    } finally {
                        output.close();
                    }
                } finally {
                    socket.close();
                }
            }
        });
    }

Demo

client

1、定义接口

2、利用动态代理,引用远程服务

3、指定代理方法

server

1、定义接口以及实现

2、利用socket和java反射,将接口暴露出去

代码

https://gitee.com/yclxiao/specialty/blob/master/javacore/src/main/java/com/ycl/blog/simplerpc/RpcServer.java