RPC實戰與核心原理

經過一個CalculatorRemoteImpl,咱們把RPC的邏輯封裝進去了,客戶端調用時感知不到遠程調用的麻煩php

download:《極客時間》RPC實戰與核心原理java

下面再來看看CalculatorRemoteImpl,代碼有些多,可是其實就是把上面的二、三、4幾個步驟用代碼實現了而已,CalculatorRemoteImpl:
負載均衡

public class CalculatorRemoteImpl implements Calculator {
    public int add(int a, int b) {
        List<String> addressList = lookupProviders("Calculator.add");
        String address = chooseTarget(addressList);
        try {
            Socket socket = new Socket(address, PORT);

            // 將請求序列化
            CalculateRpcRequest calculateRpcRequest = generateRequest(a, b);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());

            // 將請求發給服務提供方
            objectOutputStream.writeObject(calculateRpcRequest);

            // 將響應體反序列化
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            Object response = objectInputStream.readObject();

            if (response instanceof Integer) {
                return (Integer) response;
            } else {
                throw new InternalError();
            }

        } catch (Exception e) {
            log.error("fail", e);
            throw new InternalError();
        }
    }}

add方法的前面兩行,lookupProviders和chooseTarget,可能你們會以爲不明覺厲。socket

分佈式應用下,一個服務可能有多個實例,好比Service B,可能有ip地址爲198.168.1.11和198.168.1.13兩個實例,lookupProviders,其實就是在尋找要調用的服務的實例列表。在分佈式應用下,一般會有一個服務註冊中心,來提供查詢實例列表的功能。分佈式

查到實例列表以後要調用哪個實例呢,只時候就須要chooseTarget了,其實內部就是一個負載均衡策略。ide

因爲咱們這裏只是想實現一個簡單的RPC,因此暫時不考慮服務註冊中心和負載均衡,所以代碼裏寫死了返回ip地址爲127.0.0.1。spa

代碼繼續往下走,咱們這裏用到了Socket來進行遠程通信,同時利用ObjectOutputStream的writeObject和ObjectInputStream的readObject,來實現序列化和反序列化。ip

最後再來看看Server端的實現,和Client端很是相似,ProviderApp:get

public class ProviderApp {
    private Calculator calculator = new CalculatorImpl();

    public static void main(String[] args) throws IOException {
        new ProviderApp().run();
    }

    private void run() throws IOException {
        ServerSocket listener = new ServerSocket(9090);
        try {
            while (true) {
                Socket socket = listener.accept();
                try {
                    // 將請求反序列化
                    ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                    Object object = objectInputStream.readObject();

                    log.info("request is {}", object);

                    // 調用服務
                    int result = 0;
                    if (object instanceof CalculateRpcRequest) {
                        CalculateRpcRequest calculateRpcRequest = (CalculateRpcRequest) object;
                        if ("add".equals(calculateRpcRequest.getMethod())) {
                            result = calculator.add(calculateRpcRequest.getA(), calculateRpcRequest.getB());
                        } else {
                            throw new UnsupportedOperationException();
                        }
                    }

                    // 返回結果
                    ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                    objectOutputStream.writeObject(new Integer(result));
                } catch (Exception e) {
                    log.error("fail", e);
                } finally {
                    socket.close();
                }
            }
        } finally {
            listener.close();
        }
    }}

Server端主要是經過ServerSocket的accept方法,來接收Client端的請求,接着就是反序列化請求->執行->序列化執行結果,最後將二進制格式的執行結果返回給Client。it

就這樣咱們實現了一個簡陋而又詳細的RPC。  說它簡陋,是由於這個實現確實比較挫,在下一小節會說它爲何挫。說它詳細,是由於它一步一步的演示了一個RPC的執行流程,方便你們瞭解RPC的內部機制。

相關文章
相關標籤/搜索