天天看点

RPC远程过程调用的简单实现

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。比如两个不同的服务 A、B 部署在两台不同的机器上,那么服务 A 如果想要调用服务 B 中的某个方法该怎么办呢?

RPC远程过程调用的简单实现

RPC是一种通信方式:既可以基于HTTP协议实现,也可以直接在TCP协议上实现。

Client:客户端

public class Client {
    public static void main(String[] args) {
        UserService stub = (UserService) Stub.getStub(UserServiceImpl.class);
        System.out.println(stub.getUserById(1).toString());
    }
}
           

Stub:用于封装客户端侧的网络通信

实现方式,借助动态代理

public class Stub {
    public static Object getStub(Class clazz) {
        Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket("127.0.0.1",8000);

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

                String clazzName = clazz.getName();
                String methodName = method.getName();
                Class[] typeParameters = method.getParameterTypes();
				//将这些关键信息发送给Server,其借助反射进行恢复
                oos.writeUTF(clazzName);
                oos.writeUTF(methodName);
                oos.writeObject(typeParameters);
                oos.writeObject(args);
                oos.flush();

                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Object o1 = ois.readObject();

                oos.close();
                socket.close();

                return o1;
            }

        });
        return proxy;
    }
}
           

Server:服务端

public class Server {
    private static boolean running = true;
    public static void main(String[] args) {
        ServerSocket ss = null;
        try {
            ss = new ServerSocket(8000);
            while (running) {
                Socket s = ss.accept();
                process(s);
                s.close();
            }
            ...
    }

    private static void process(Socket s){
        ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
        String clazzName = ois.readUTF();
        String methodName = ois.readUTF();
        Class[] parameterTypes = (Class[]) ois.readObject();
        Object[] args = (Object[]) ois.readObject();
		//获取指定的Class对象
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Class<?> clazz = classLoader.loadClass(clazzName);
		//借助反射执行方法
        Method method = clazz.getMethod(methodName, parameterTypes);
        Object o = method.invoke(clazz.newInstance(), args);

        ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
        oos.writeObject(o);
        oos.flush();
    }
}
           

这里欣赏下常用反射函数

在利用反射访问private、protected成员变量和方法时必须通过setAccessible(boolean access)方法取消Java语言检查,否则将抛出IllegalAccessException。

ClassLoader loader = Thread.currentThread().getContextClassLoader();   
        Class<?> clazz = loader.loadClass("com.biezhi.ioc.PrivateHero");   
        PrivateHero hero = (PrivateHero) clazz.newInstance();
    	Field name = clazz.getDeclaredField("name");
        // 取消Java语言访问检查以访问private变量
        name.setAccessible(true);
        name.set(hero, "德玛西亚之力"); //要不这里报错
           

常见的序列化方式

序列化:将对象状态转换为可保持或传输的二进制。

反序列化:将二进制数据转换为对象的过程。

RPC远程过程调用的简单实现

推荐protobuf、thrift、Hessian(PS:反正不要用JDK原生的就对了)