RPC原理以及實踐

1. 認識RPC

RPC(Remote Procedure Call)是指遠程過程調用,在微服務架構中,各個微服務之間的通信一般使用RPC,比如用戶微服務和訂單微服務,需求是在用戶微服務中查詢當前用戶的訂單,此時用戶服務是RPC的請求端,訂單微服務是RPC的服務端。

2. RPC原理

2.1 要實現RPC有3個難點問題:
①如何讓客戶端知道服務端提供的RPC接口以及方法?
方法一:客戶端和服務端在pom文件中同時依賴定義了接口的依賴,服務端需要實現這些接口,提供服務
方法二:比如Spring Cloud Feign是將請求通過動態代理加工成HTTP請求,返回時再將Response反加工過程
②客戶端和服務端如何通信?
方法一:Java可以通過NIO/BIO
③客戶端如何將參數傳遞到服務端?
將數據序列化獲得二進制數據進行傳輸 序列化的技術也很多java默認序列化/Json/XML/Protobuf/Hessian 序列化和反序列化效率對比
在這裏插入圖片描述

3. 手寫RPC框架

3.1 RPC Client

main線程

public static void main( String[] args )
{
	IOrderService orderService = null;
	RpcProxyClient rpcProxyClient = new RpcProxyClient();
    //代理類的實現
	orderService = rpcProxyClient.clientProxy(IOrderService.class,"localhost",8080);
	System.out.println(orderService.getOrderList());
}

代理類

public class RpcProxyClient {
    public <T>T clientProxy(final Class<T> interfaceCls, String host, int port){
    //爲了更簡潔,創建一個繼承InvocationHandler的子類實現具體邏輯
        return (T)Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class<?>[]{interfaceCls}, new RpcInvocationHandler(host, port));
    }
}

InvocationHandler的子類

public class RpcInvocationHandler implements InvocationHandler {
    private String host;
    private int port;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //通過socket創建連接
        RpcNetTransport rpcNetTransport = new RpcNetTransport(host,port);
        rpcNetTransport.createSocket();
        //傳輸請求的數據 請求接口名稱,方法名稱,參數等
        RpcRequest request = new RpcRequest();
        request.setArgs(args);
        request.setMethodName(method.getName());
        request.setTypes(method.getParameterTypes());
        request.setClassName(method.getDeclaringClass().getName());
        return rpcNetTransport.send(request);
    }
}
3.2 服務端

main線程

public static void main( String[] args ) throws IOException {
	IOrderService service = new OrderImpl();
	RpcProxyServer rpcProxyServer = new RpcProxyServer();
	rpcProxyServer.publisher(service,8080);
}

RpcProxyServer接收客戶端請求並處理

public class RpcProxyServer {
    //使用多線程防止阻塞
    private final ExecutorService executorService = Executors.newCachedThreadPool();
    
    public void publisher(Object service, int port){
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(port);
            while (true){
                socket = serverSocket.accept();
                //具體的處理類
                executorService.submit(new ProcessHandler(socket, service));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ProcessHandler 反序列化處理以及反射調用目標方法

public class ProcessHandler implements Runnable {

    private Socket socket;
    private Object service;

    public ProcessHandler(Socket socket, Object service) {
        this.socket = socket;
        this.service = service;
    }

    @Override
    public void run() {
        ObjectInputStream objectInputStream = null;
        ObjectOutputStream objectOutputStream = null;
        InputStream inputStream = null;

        try {
            inputStream = socket.getInputStream();
            objectInputStream = new ObjectInputStream(inputStream);
            RpcRequest request = (RpcRequest)objectInputStream.readObject();
            //反射調用目標方法
            Object rs = invoke(request);
            System.out.println("服務端處理執行的結果" + rs);
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(rs);
            objectOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } finally {
            //TODO 關閉流
        }
    }

    private Object invoke(RpcRequest request) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class clazz = Class.forName(request.getClassName());
        Method method = clazz.getMethod(request.getMethodName(),request.getTypes());
        return method.invoke(service, request.getArgs());
    }
}

4. 改進的方向

  • 只支持java語言,耦合性比較強。可以採用json或者xml進行數據傳輸
  • Java NIO/BIO效率比較低,可以採用Netty BIO傳輸
  • Java序列化數據量大,可以考慮其他方式Hessian/Protobuf/Avro
  • 使用註解方式標識需要RPC的服務端和客戶端
  • 考慮如何做成中間件的形式

5. 現有的RPC框架

  • Hessian
  • Thrift
  • Dubbo
  • Motan
  • gRPC

代碼github鏈接:RPC V1

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