Java Distributed Component - RPC (Handwritten RPC)

Keywords: socket network Java Programming

RPC

Remote Procedure Call - A remote call procedure. It is a protocol that requests services from remote computer programs over a network without understanding the underlying network technology.

constitute

  1. Network protocol

    TCP or UDP Socket Programming

  2. Input and output streams (object streams)

    ObjectOutputStream: Writes basic data types and graphics of Java objects to OutputStream. ObjectInputStream can be used to read (refactor) objects. Persistent storage of objects can be achieved by using files in streams. If the stream is a network socket stream, the object can be reconstructed on another host or in another process.
    ObjectInputStream: Deserialize the basic data and objects written by ObjectOutputStream.

  3. Dynamic proxy

    Dynamic proxy technology is used to generate an object's proxy object.

java handwritten RPC

Server provides service invocation and service realization to expose service interface

/**
 * RPC--Server-side Basic Method
 */
public interface Server {
    /**
     * Socket port
     */
    int PORT = 8080;

    /**
     * Start the server
     */
    void start() throws IOException;

    /**
     * Stop Server
     */
    void stop();

    /**
     * Service registration
     * @param serviceInterface  -- Exposure Service Interface
     * @param impl  --  Internal implementation class
     */
    void regist(Class<? extends IRpcService> serviceInterface, Class<? extends IRpcService> impl);
}
public class ServerCenter implements Server {

    /**
     * Thread pool receives client calls
     */
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 20, 200, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
    /**
     * Service Registration Cache
     */
    private static final HashMap<String, Class<?>> serviceRegistry = new HashMap<String, Class<?>>();

    /**
     * Startup service
     */
    @Override
    public void start() throws IOException {
        ServerSocket server = new ServerSocket();
        server.bind(new InetSocketAddress(PORT));
        try {
            for (;;) {
                executor.execute(new ServiceTask(server.accept()));
            }
        } finally {
            server.close();
        }
    }

    /**
     * Out of Service
     */
    @Override
    public void stop() {
        executor.shutdown();
    }

    /**
     * Registration service
     */
    @Override
    public void regist(Class<? extends IRpcService> serviceInterface, Class<? extends IRpcService> impl) {
        serviceRegistry.put(serviceInterface.getName(), impl);
    }

    /**
     *Service Specific Scheduling - Object Stream Deserialization, Reflective Invocation of Local Services, and Output to Client
     */
    private static class ServiceTask implements Runnable {
        Socket clent = null;

        public ServiceTask(Socket client) {
            this.clent = client;
        }

        @Override
        public void run() {
            ObjectInputStream input = null;
            ObjectOutputStream output = null;
            try {
                input = new ObjectInputStream(clent.getInputStream());
                String serviceName = input.readUTF();
                String methodName = input.readUTF();
                Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
                Object[] arguments = (Object[]) input.readObject();
                Class<?> serviceClass = serviceRegistry.get(serviceName);
                if (serviceClass == null) {
                    throw new ClassNotFoundException(serviceName + " not found");
                }
                Method method = serviceClass.getMethod(methodName, parameterTypes);
                Object result = method.invoke(serviceClass.newInstance(), arguments);
                // 3. Deserialize the execution result and send it to client through socket
                output = new ObjectOutputStream(clent.getOutputStream());
                output.writeObject(result);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (output != null) {
                    try {
                        output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (input != null) {
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (clent != null) {
                    try {
                        clent.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}

Server exposes service interfaces

/**
 * Add generics, specification calls
 */
public interface IRpcService extends Serializable{

}

/**
 * The service side exposes the service interface, and the client can call it directly.
 */
public interface IHelloService extends IRpcService{
    String sayHi(String name, String msg);
}

Implementation of Server Interface

/**
 * Implementation of Server Interface
 */
public class HelloServiceImpl implements IHelloService {

    private static final long serialVersionUID = 1L;

    @Override
    public String sayHi(String name, String msg) {
        return new StringBuffer().append("hi~! ").append(name).append(",").append(msg).toString();
    }

}

Client calls RPC service

public class Client {
    /**
     * Client proxy access remote service object
     * @param serviceInterface
     * @param addr
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T extends IRpcService> T getRemoteProxyObj(Class<? extends IRpcService> serviceInterface,InetSocketAddress addr){
        return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = null;
                ObjectOutputStream output = null;
                ObjectInputStream input = null;
                try {
                    // 1. Create a Socket client to connect to a remote service provider according to the specified address
                    socket = new Socket();
                    socket.connect(addr);

                    // 2. Encoding the interface class, method name, parameter list needed for remote service invocation and sending them to service provider
                    output = new ObjectOutputStream(socket.getOutputStream());
                    output.writeUTF(serviceInterface.getName());
                    output.writeUTF(method.getName());
                    output.writeObject(method.getParameterTypes());
                    output.writeObject(args);

                    // 3. Synchronized blocking waits for the server to return the reply, and returns after obtaining the reply.
                    input = new ObjectInputStream(socket.getInputStream());
                    return input.readObject();
                } finally {
                    if (socket != null) socket.close();
                    if (output != null) output.close();
                    if (input != null) input.close();
                }
            }

        });
    }
}

test

public class RpcTest {
    public static void main(String[] args) throws IOException {
        IHelloService service = Client.getRemoteProxyObj(IHelloService.class, new InetSocketAddress("localhost", 8080));
        System.out.println(service.sayHi("Zhang San", "Happy New Year!!"));
    }
}

Posted by joozt on Wed, 06 Feb 2019 04:06:17 -0800