From 0 to write a lightweight distributed RPC-based annotation framework (1) RPC principles and preparation

1. Principle

RPC (Remote Procedure Call Protocol) remote procedure call, distributed basis.

GIT specific source has been uploaded annotation-based RPC Source
RPC remote service call is just like calling the same local interface.
We look at a simple and classic example of RPC

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;
public class RpcFramework {
    /**
     * 暴露服务
     * 
     * @param service 服务实现
     * @param port 服务端口
     * @throws Exception 201722213:33:02 TODO: 线程池
     */
    public static void export(final Object service, int port) throws Exception {
        if (service == null)
            throw new IllegalArgumentException("service instance == null");
        if (port <= 0 || port > 65535)
            throw new IllegalArgumentException("Invalid port " + port);
        LogCore.RPC.info("Export service={}, port={}", service.getClass().getName(), port);
        @SuppressWarnings("resource")
        ServerSocket server = new ServerSocket(port);
        while (true) {// for (;;)
            /*
             * 阻塞,直到有个链接连过来,这里连上后又阻塞等待链接,所以是个1对n的服务。 调用这方法的函数会被阻塞,这个方法里的线程是为了实现同时1对多,也因此server.accept()不能放在try-resource块里,那样会提前关闭socket
             */
            Socket socket = server.accept();
            try {
                ThreadPoolTool.execute(() -> {
                    try (ObjectInputStream input = new ObjectInputStream(socket.getInputStream())) {
                        String methodName = input.readUTF();
                        Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
                        Object[] arguments = (Object[]) input.readObject();
                        try (ObjectOutputStream output = new ObjectOutputStream(
                                    socket.getOutputStream());) {
                            Method method = service.getClass().getMethod(methodName, parameterTypes);
                            Object result = method.invoke(service, arguments);
                            output.writeObject(result);
                        } catch (Throwable t) {
                            LogCore.RPC.error("rpc framework output err", t);
                        }
                    } catch (Throwable t) {
                        LogCore.RPC.error("rpc framework err", t);
                    }
                });
                // thread.setName("RpcFramework export service:" + service.getClass().getSimpleName());
                // thread.start();
            } catch (Exception e) {
                LogCore.RPC.error("rpc framework socket err", e);
            }
        }
    }

    /**
     * 引用服务
     * 
     * @param <T> 接口泛型
     * @param interfaceClass 接口类型
     * @param host 服务器主机名
     * @param port 服务器端口
     * @return 远程服务
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) {
        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);
        LogCore.RPC.info("Get remote service {} from server {}:{}", interfaceClass.getName(), host, port);
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] { interfaceClass },
                    new InvocationHandler() {
                        public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
                            try (Socket socket = new Socket(host, port)) {
                                try (ObjectOutputStream output = new ObjectOutputStream(
                                            socket.getOutputStream())) {
                                    output.writeUTF(method.getName());
                                    output.writeObject(method.getParameterTypes());
                                    output.writeObject(arguments);
                                    try (ObjectInputStream input = new ObjectInputStream(
                                                socket.getInputStream())) {
                                        Object result = input.readObject();
                                        if (result instanceof Throwable) {
                                            throw (Throwable) result;
                                        }
                                        return result;
                                    }
                                }
                            }
                        }
                    });
    }

}

In the above example, the use of traditional IO and achieve concurrent calls with multiple threads, using JDK serialization. The process is

1. Socket IO communication using a communication.

2. Service consumers will be called the class interfaces and methods and parameters to be called

① to Agent
② The proxy interface method names and parameter types and parameter values ​​of the sequence
③ agent sends the serialized information to a remote network, and accept the results
④ agent will return a result after deserialization

3. Service providers turn Socket Server is done by the reflection method call

① server open Socket server, blocking socket.accept () until a client connects.
② After receiving the request, opening up the thread processing information coming from this link. And to continue blocking new socket.accept ();
③ connecting deserialize the information coming from the method name, parameter type, the parameter value method. Calling method reflected by the instance, and returns the result of the sequence. Call ended.

Of course the above example an example occupies a port. But our understanding of the principles and ideas RPC is enough: Consumers using agent technology call the local agent. Service providers use reflection to invoke a local instance.
We will then we can start to write a more complete RPC framework from 0

2. Preparation

(java+maven+zookeeper+netty+springboot(IOC))

demand Program Explanation
Construction of the project maven
Communication netty netty Use the netty deal with http, the freedom to choose TCP or HTTP
Serialization fixed json Simple, simplify development
Registration and discovery services zookeeper zookeeper maintains active discovery session connections may be disconnected deletes client node, zk CAP ensures the CP, only the client and wherein JDK zkclient serialized serialized String replace
IOC container Spring boot Using only its most basic part of the IOC.
Published 327 original articles · won praise 82 · views 890 000 +

Guess you like

Origin blog.csdn.net/kkgbn/article/details/77283141