RPC極簡單實現

爲什麼有了HTTP還要使用RPC

  • HTTP是被設計用來在桌面瀏覽器上的
  • HTTP是無狀態的、每次傳輸都需要攜帶報文頭,會造成一部分額外的網絡開銷
  • 適用於接口不多的情況下,簡單,直接;
  • RPC會有註冊中心,有豐富的監控中心,提供軟負載均衡,動態擴展,還有安全等

最重要的一點,RPC是一套理論,使用HTTP協議也可以實現RPC,RPC是解耦的一種方式!!!

RPC需要解決的問題

  • 服務調用問題(服務的註冊與發現)
  • 遠程代理問題(jdk動態代理,屏蔽網絡問題)
  • 通信問題(BIO)
  • 序列化問題(jdk序列號)
  • 註冊服務的實例化(反射)

我的簡單實現步驟是這樣的:

  1. client通過代理拿到對象;

  2. 當實際訪問方法時,通過jdk動態代理拿到(接口名,方法名,參數類型,參數值);

  3. 然後將數據寫入socket中,發往server 端;

  4. server端接受到以後使用同樣的順序解析數據,通過接口名拿到持有的實現類對象 ;

  5. 再通過反射調用對應的服務方法;

  6. 將方法結果發給client;

下面是代碼解釋:

1.Server接受客戶端請求,並處理請求

@Override
public void run() {
    try (ObjectInputStream inputStream = new ObjectInputStream(client.getInputStream());
            ObjectOutputStream outputStream = new ObjectOutputStream(client.getOutputStream())) {
        // 接受客戶端請求
        String serviceName = inputStream.readUTF();
        String methodName = inputStream.readUTF();
        Class<?>[] paraType = (Class<?>[]) inputStream.readObject();
        Object[] args = (Object[]) inputStream.readObject();

        // 進行業務處理,並返回結果
        Class serviceClass = serviceHolder.get(serviceName);
        if (null == serviceClass) {
            throw new ClassNotFoundException(serviceClass + " not found!");
        }
        Method method = serviceClass.getMethod(methodName, paraType);
        Object result = method.invoke(serviceClass.newInstance(), args);

        outputStream.writeObject(result);
        outputStream.flush();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

2. 服務的持有容器使用HashMap(key=服務名,Class = 實際服務的Class類)

Map<String, Class> serviceHolder = new HashMap<>();

3. client 端調用實現

public class RPCClientFrame {
    public static <T> T getRemoteProxyObj(final Class<?> serviceInterface, final InetSocketAddress address) {
        return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[] { serviceInterface },
                new DynProxy(serviceInterface, address));
    }

    private static class DynProxy implements InvocationHandler {
        private final Class<?> serviceInterface;
        private final InetSocketAddress address;

        public DynProxy(Class<?> serviceInterface, InetSocketAddress address) {
            this.serviceInterface = serviceInterface;
            this.address = address;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Socket socket = null;
            ObjectOutputStream output = null;
            ObjectInputStream input = 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); //參數值

                output.flush();

                input = new ObjectInputStream(socket.getInputStream());
                return input.readObject();
            }finally {
                if(output !=null) output.close();
                if(input !=null) input.close();
                if(socket !=null) socket.close();
            }
        }
    }
}

4. 測試代碼

@Test
public void shouldAnswerWithTrue() {
    InetSocketAddress address = new InetSocketAddress("127.0.0.1", 9000);
    Service service = RPCClientFrame.getRemoteProxyObj(Service.class, address);
    String res = service.recivedParams("abc", 1000);
    assertEquals("abc|1000", res);
}

5.Service的實現類

public class ServiceImpl implements Service {

    @Override public String recivedParams(String a, long b) {
        return "params:"+a +"|" +b;
    }
}

說明:此實現是最簡單的一種實現(直連),未考慮其上層的內容,但對於其核心的實現原理基本解釋清楚了。

如有問題,可私信

 

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