經過一個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的內部機制。