不會手寫簡單的RPC框架,敢說自己學過RPC?

RPC(Remote Proceduce Call 遠程過程調用) 一般用來實現部署在不同機器上的系統之間的方法調用,使程序能夠像訪問本地系統資源一樣,通過網絡傳輸過去訪問遠端系統資源。

基礎概念

在這裏插入圖片描述

遠程調用分爲本地調用端遠程服務端

  • 調用者根據服務接口獲得對應的代理對象,然後直接調用接口的方法即可獲得返回結果,可以實現像調用本地服務一樣調用遠程服務;
  • 本地調用端主要通過動態代理的方式來實現上述功能,調用接口方法的時候,其代理對象實現了具體的網絡通訊細節,將接口名、方法名、方法參數等請求信息發送給遠程服務端並等待遠程服務端的返回信息;
  • 遠程服務端根據請求信息通過反射獲得具體的服務實現類,執行實現類的相應方法後並將調用結果返回給調用端;調用端接收到返回值,代理對象將其封裝爲返回結果給調用者, 整個遠程調用即結束。

代碼實現

  • 遠程服務接口
public interface HelloService {
    public String sayHi(String name);
}
  • 遠程服務接口實現類
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHi(String name) {
       return "hi,"+name;
    }
}
  • 服務端發佈服務
public class RPCServerTest {
    public static void main(String[] args) throws Exception {
        Server server = new ServerCenter(8888);
        //將服務端的接口信息註冊到註冊中心
        server.register(HelloService.class, HelloServiceImpl.class);
        server.start();
    }
}
  • 服務註冊中心接口
public interface Server {
    public void start();
    public void stop();
    //註冊服務
    public void register(Class<?> service,Class serviceImpl);
}
  • 服務註冊中心實現類
public class ServerCenter implements Server {
    //serviceRegiser 存儲了服務名稱和服務對象的關係。
    private static HashMap<String,Object> serviceRegiser = new HashMap<>();
    private static int port;
    private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    private static boolean isRunning = false;

    public ServerCenter(int port){
        this.port = port;
    }
    @Override
    public void start(){
        ServerSocket server = null;
        try {
            server = new ServerSocket();
            server.bind(new InetSocketAddress(port));
        }catch (Exception e){
            e.printStackTrace();
        }

        isRunning = true;

        while (true){
                System.out.println("sart server....");
                Socket socket = null;
                try {
                    //等待客戶端連接
                    socket = server.accept();
                    executor.execute(new ServiceTask(socket));
                }catch (Exception e){
                    e.printStackTrace();
                }
        }

    }

    @Override
    public void stop() {
        isRunning = false;
        executor.shutdown();
    }

    @Override
    public void register(Class<?> service, Class serviceImpl) {
        serviceRegiser.put(service.getName(),serviceImpl);
    }


    static class ServiceTask implements Runnable{
        private Socket socket;
        public ServiceTask(Socket socket) {
            this.socket = socket;
        }
        @Override
        public void run() {
            ObjectInputStream input = null;
            ObjectOutputStream output = null;
            try {
                System.out.println("收到客戶端連接請求,處理該請求---------");
                //收到客戶端連接請求,處理該請求
                input = new ObjectInputStream(socket.getInputStream());
                String serviceName  = input.readUTF();
                String methodName = input.readUTF();
                Class[] parameterTypes = (Class[])input.readObject();
                Object[] arguments = (Object[])input.readObject();

                Class ServiceClass  = (Class) serviceRegiser.get(serviceName);
                Method method = ServiceClass.getMethod(methodName,parameterTypes);
                Object result = method.invoke(ServiceClass.newInstance(),arguments);
                output = new ObjectOutputStream(socket.getOutputStream());
                output.writeObject(result);
            } catch (Exception e){
                e.printStackTrace();
            }finally {
                try {
                    if(output != null) output.close();
                    if (input != null) input.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 客戶端代理實現
public class Client {
    public static <T>  T getRemoteProxyObj(Class serviceInterface, InetSocketAddress address){
        return (T)Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) {

                ObjectInputStream input = null;
                ObjectOutputStream output = null;
                Socket socket = null;
                try {
                    //客戶端向服務端發送請求,請求具體的某一個端口
                    socket = new Socket();
                    socket.connect(address);

                    output = new ObjectOutputStream(socket.getOutputStream()); //發送序列流
                    output.writeUTF(serviceInterface.getName());
                    output.writeUTF(method.getName());
                    output.writeObject(method.getParameterTypes());
                    output.writeObject(args);

                    //等待服務端處理...
                    input = new ObjectInputStream(socket.getInputStream());

                    return input.readObject();
                }catch (Exception e){
                    e.printStackTrace();
                    return null;
                }finally {
                    try {
                        if(output != null) output.close();
                        if (input != null) input.close();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        });
    }
}
  • 構建一個Socket,連接遠程服務。
  • 向遠程服務發送數據。(方法名和方法參數)
  • 接收遠程服務響應的數據。

  • 客戶端調用
public class RPCClientTest {
    public static void main(String[] args) throws ClassNotFoundException {
        HelloService service1 = Client.getRemoteProxyObj(Class.forName("network.rpc.server.HelloService"),new InetSocketAddress("127.0.0.1",8888));
        System.out.println(service1.sayHi("Carroll"));
    }
}

你知道的越多,你不知道的越多。
有道無術,術尚可求,有術無道,止於術。
如有其它問題,歡迎大家留言,我們一起討論,一起學習,一起進步

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章