通過手寫簡易版RPC框架理解其原理

什麼是RPC框架?

  • RPC:remote procedure call 即:遠程過程調用
  • 在分佈式架構中離不開服務之間的通信
  • 爲了提高服務之間通信的性能:產生了如Dubbo、webservice、Thrift等RPC框架

簡易版RPC框架實現

  • 通過實現一個簡易版本的RPC框架,去學習其原理
  • 首先我們創建需要一個服務端

服務端實現

  • rpc-server:以quickstart的方式快速創建一個maven工程
image-20200530151137794
  • 以同樣的方式創建rpc-server-api和rpc-server-provider
  • 創建api和provider模塊的作用是在本機模擬服務之間的調用
    • rpc-server-api:定義客戶端需要拿到的一些信息,如接口、傳輸對象等打成jar包作爲依賴使用
    • rpc-server-provider:服務提供方基於api的實現,提供服務供客戶端進行調用。
image-20200530151246257
  • 至此我們的服務端基礎架構就搭建完成了

API實現

  • 寫一個簡單的接口
public interface UserService {

    String handToUser(String content);

    String saveUser(User user);

}
image-20200530153353766

Provider實現

  • 依賴api(需要把api打成jar包到本地倉庫)
<dependency>
    <groupId>com.self.struggle</groupId>
    <artifactId>rpc-server-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
  • 服務簡單實現
public class UserServiceImpl implements UserService {

    @Override
    public String handToUser(String s) {
        return "hi,"+s+".Welcome come!";
    }

    @Override
    public String saveUser(User user) {
        return user.toString();
    }

}
  • 我們的服務端實現是要提供給客戶端調用的,所以我們需要暴露出我們的服務實現
  • 提供服務暴露的方法—使用代理的形式暴露服務(基於socket實現)
public class RpcServerProxy {

    /**  線程池回顧
     *   newCached:可緩存地線程池,核心線程數爲0,最大線程數爲 Integer.MAX_VALUE
     *              在回收時間內,可以對創建好地線程進行復用
     */
    ExecutorService executorService = Executors.newCachedThreadPool();

    public void publish(int port) {
        ServerSocket serverSocket = null;
        try {
            //  port:通過端口暴露服務
            serverSocket = new ServerSocket(port);
            //  不斷地接受請求
            while (true) {
                //  建立socket連接:通過ois oos去處理    accept:阻塞
                Socket socket = serverSocket.accept();
                //  利用線程池來處理請求   每一個socket交給一個DealRequest:具體地處理邏輯
                executorService.execute(new DealRequest(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
  • DealRequest實現
public class DealRequest implements Runnable {

    private Socket socket;

    public DealRequest(Socket socket) {
        this.socket = socket;
    }

    /**
     * 處理socket連接中地流信息
     */
    @Override
    public void run() {
        ObjectInputStream objectInputStream = null;
        ObjectOutputStream objectOutputStream = null;

        try {
            //  獲取客戶端發送請求中地輸入流
            objectInputStream = new ObjectInputStream(socket.getInputStream());

            /*  輸入流包含的內容
             *  請求的目標類,方法名稱,參數  在 api 中定義一個對象,接收返回信息
             */

            //  反序列化過程
            RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject();

            //  通過反射  實現調用
            Object result = invoke(rpcRequest);

            //  寫入結果
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(result);
            objectOutputStream.flush();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(objectInputStream != null) {
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(objectOutputStream != null) {
                try {
                    objectOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private Object invoke(RpcRequest request) throws Exception {
        // 反射調用
        Object[] params = request.getParams();
        Class<?>[] types = new Class[params.length];
        for (int i = 0; i < params.length; i++) {
            types[i] = params[i].getClass();
        }
        Class<?> clazz = Class.forName(request.getClassName());
        Object obj = clazz.newInstance();
        Method method = clazz.getMethod(request.getMethodName(), types);
        return method.invoke(obj, params);
    }

}

發佈服務

  • 在App.java中發佈服務
public class App
{
    public static void main( String[] args )
    {
        RpcServerProxy rpcServerProxy = new RpcServerProxy();
        rpcServerProxy.publish(8080);
    }
}
  • 至此,簡易版的服務端實現完成

客戶端實現

  • 客戶端需要去調用服務端
  • 通過遠程代理服務調用

代理服務實現

  • 通過反射的方式進行調用,具體實現在 RemoteInvocationHandler 中
public class RpcClientProxy {

    public Object clientProxy(final Class<?> interfaceCls, final String host, final int port) {
        return Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class<?>[]{interfaceCls}, new RemoteInvocationHandler(host, port));
    }

}
  • RemoteInvocationHandler
  • 在invoke方法中構建傳輸對象,由RpcNetTransport發送請求
public class RemoteInvocationHandler implements InvocationHandler {

    private String host;
    private int port;

    public RemoteInvocationHandler(String host, int port) {
        this.host = host;
        this.port = port;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        RpcRequest rpcRequest = new RpcRequest();
        rpcRequest.setClassName(method.getDeclaringClass().getName());
        rpcRequest.setMethodName(method.getName());
        rpcRequest.setParams(args);

        //  遠程通信
        RpcNetTransport rpcNetTransport = new RpcNetTransport(host, port);

        return rpcNetTransport.send(rpcRequest);
    }
}
  • RpcNetTransport
public class RpcNetTransport {

    private String host;
    private int port;

    public RpcNetTransport(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public Object send(RpcRequest request) {
        Socket socket = null;
        Object result = null;
        ObjectOutputStream objectOutputStream = null;
        ObjectInputStream objectInputStream = null;

        try {
            //  建立連接
            socket = new Socket(host, port);
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(request);
            objectOutputStream.flush();

            objectInputStream = new ObjectInputStream(socket.getInputStream());
            result = objectInputStream.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

}

服務調用

public class App
{
    public static void main( String[] args )
    {
        UserService userService = (UserService) new RpcClientProxy().clientProxy(UserService.class, "localhost", 8080);

        String status = userService.handToUser("你好");

        System.out.println("server back:"+status);
    }
}

驗證結果

  • 客戶端
    在這裏插入圖片描述

  • 服務端

在這裏插入圖片描述

流程圖

  • nexus代表私服倉庫
  • ProxyClient:客戶端代理,socket建立連接後,通過動態代理的方式進行遠程調用
  • provider:業務邏輯實現,對api進行處理
  • RpcproxyServer:服務端代理,暴露服務,供客戶端調用

在這裏插入圖片描述

源碼和升級版本

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