国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

一個簡單的"RPC框架"代碼分析

2019-11-15 01:08:33
字體:
來源:轉載
供稿:網(wǎng)友
一個簡單的"RPC框架"代碼分析

0,服務接口定義---Echo.java

/* * 定義了服務器提供的服務類型 */public interface Echo {    public String echo(String string);}

一,客戶端代碼分析--實現(xiàn)類:MainClient.java

客戶端實現(xiàn)包括:獲得一個代理對象,并使用該代理對象調用服務器的服務。獲取代理對象時,需要指定被代理的類(相當于服務器端提供的服務名),Server ip,Port,這樣客戶端就能找到服務端的服務了。

延伸:分布式環(huán)境下,Client如何打到Server的服務?---因為,在服務器中運行的某些服務不像標準服務有著固定的端口,如HTTP的80端口。

一種解決方法是:在運行服務的每臺機器上都運行一個特殊的守護進程,該守護進程負責跟蹤位于該機器中每一項服務所使用的端口;此外,守護進程還監(jiān)聽一個特定的已經(jīng)端口,Client通過這個端口與守護進程聯(lián)系,請求得到指定服務的端口。

復雜的RPC實現(xiàn)框架中,比如可以把服務注冊到ZooKeeper中,Client也從ZooKeeper中查詢服務。參考:一個更復雜的RPC框架實現(xiàn)

Echo echo = RPC.get

二,服務器端分析--實現(xiàn)類:MainServer.java

服務器實現(xiàn)包括:創(chuàng)建一個服務器對象,將它能提供的服務注冊,并啟動進程監(jiān)聽客戶端的連接

        Server server = new RPC.RPCServer();        /*         * server 啟動后,需要注冊server端能夠提供的服務,這樣client使用 服務的名字、         * 服務器的IP、以及服務所運行的端口 來調用 server 的服務         */        server.register(Echo.class, RemoteEcho.class);//注冊服務的名字        server.register(AnOtherEchoService.class, AnOtherEchoServiceImpl.class);                server.start();//啟動server

三,服務器監(jiān)聽Client連接分析----實現(xiàn)類:Listener.java

當server.start()后,它要創(chuàng)建一個Listener對象,這是一個線程類,該線程用來監(jiān)聽Client連接。

public void start() {            System.out.println("啟動服務器");                        /*             * server 啟動時,需要Listener監(jiān)聽是否有client的請求連接             * listener 是一個線程,由它來監(jiān)聽連接             */            listener = new Listener(this);            this.isRuning = true;            listener.start();//listener 是一個線程類,start()后會執(zhí)行線程的run方法        }

其實,監(jiān)聽連接就是JAVA ServerSocket類和Socket類提供的相關功能而已。

/* * accept()是一個阻塞方法,server_socket 一直等待client 是否有連接到來 */ Socket client = server_socket.accept();//建立一條TCP連接

四,動態(tài)代理對象 生成---RPC.java

客戶端只需要編寫生成代理對象,用代理對象去調用遠程服務的代碼即可。但是,底層的功能如:建立連接,序列化(本例中也沒有考慮),跨語言調用(未考慮)...是由RPC框架完成的。

當MainClient 語句:RPC.getProxy(Echo.class, "127.0.0.1", 20382);執(zhí)行時,會由

/*         * @param Class[]{} 該參數(shù)聲明了動態(tài)生成的代理對象實現(xiàn)了的接口,即 clazz 所代表的接口類型 .         * 這表明了生成的代理對象它是一個它所實現(xiàn)了的接口類型的對象         * 從而就可以用它來調用它所實現(xiàn)的接口中定義的方法         *          * @param handler 生成代理實例對象時需要傳遞一個handler參數(shù)         * 這樣當該 代理實例對象調用接口中定義的方法時,將會委托給InvocationHandler 接口中聲明的invoke方法         * 此時,InvocationHandler 的invoke 方法將會被自動調用         */        T t = (T) Proxy.newProxyInstance(RPC.class.getClassLoader(), new Class[] {clazz}, handler);        return t;

返回該代理對象,然后就會委托第三個參數(shù) handler 自動執(zhí)行 invoke(),invoke將客戶端調用的所有相關信息封裝到Invocation 對象中(后面分析)。然后執(zhí)行第16行代碼發(fā)起連接。

 1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 2                 Invocation invo = new Invocation(); 3                 invo.setInterfaces(clazz); 4                  5                 //利用反射機制將java.lang.reflect.Method 所代表的方法名,參數(shù) 封裝到 Invocation invo對象中 6                 invo.setMethod(new org.jy.rpc.protocal.Method(method.getName(),method.getParameterTypes())); 7                 invo.setParams(args); 8                  9                 /*10                  * 當把需要調用的遠程server端的方法名和參數(shù)封裝到invo之后,Client 對象 就可以把 invo 作為參數(shù) 傳遞給服務器了.11                  * 為什么需要這樣做呢?InvocationHandler 的invoke方法是自動執(zhí)行的,在該方法里面,它根據(jù)生成的代理對象 proxy (第一個參數(shù))12                  * 所實現(xiàn)的接口(由 Proxy.newProxyInstance()的第二個參數(shù)指定) 就可以知道這個接口中定義了哪些方法13                  * InvocationHandler 的 invoke 方法的第二個參數(shù)Method method 就可以解析出接口中的方法名和參數(shù)了14                  * 把它們封裝進Invocation invo對象中,再將 invo 作為 client.invoke(invo)的參數(shù) 發(fā)送到服務器方15                  */16                 client.invoke(invo);//invoke 先調用init發(fā)起一個Socket連接,再將invo 發(fā)送至輸出流中17                 return invo.getResult();18             }

五,“客戶端存根”--Client.java

最重要的是它的 invoke方法(注意與InvocationHandler的invoke()區(qū)分)。它負責建立連接,打開輸入、輸出流,向服務器發(fā)送字節(jié)數(shù)據(jù)。

1     public void invoke(Invocation invo) throws UnknownHostException, IOException, ClassNotFoundException {2         init();3         System.out.println("寫入數(shù)據(jù)");4         oos.writeObject(invo);//將Client 需要調用的Server的 接口、方法、參數(shù) 封裝起來 發(fā)給服務器5         oos.flush();6         ois = new ObjectInputStream(socket.getInputStream());//用來接收從 server 返回 回來的執(zhí)行結果 的輸入流7         Invocation result = (Invocation) ois.readObject();8         invo.setResult(result.getResult());//將結果 保存到 Invocation result對象中9     }

六,“服務器存根“---實現(xiàn)類:RPCServer.java

上面提到,服務器通過Listener監(jiān)聽客戶端連接,當建立客戶端連接后,Socket client = server_socket.accept(); 不再阻塞,服務器調用它的call()方法完成客戶端請求的功能。也即,客戶端請求的結果實際上是在服務器執(zhí)行生成的。返回的結果是在Client.java 的 invoke() 方法里被讀取出來 。call()再一次用到了JAVA反射(第11行) 參考:JAVA動態(tài)代理

 1 public void call(Invocation invo) { 2             System.out.println(invo.getClass().getName()); 3             Object obj = serviceEngine.get(invo.getInterfaces().getName()); 4             if(obj!=null) { 5                 try { 6                     Method m = obj.getClass().getMethod(invo.getMethod().getMethodName(), invo.getMethod().getParams()); 7                     /* 8                      * 利用JAVA反射機制來執(zhí)行java.lang.reflect.Method 所代表的方法 9                      * @param result : 執(zhí)行實際方法后 得到的 服務的執(zhí)行結果10                      */11                     Object result = m.invoke(obj, invo.getParams());12                     invo.setResult(result);//將服務的執(zhí)行結果封裝到invo對象中。在后面的代碼中,將該對象寫入到輸出流中13                 } catch (Throwable th) {14                     th.printStackTrace();15                 }16             } else {17                 throw new IllegalArgumentException("has no these class");18             }19         }

七,”RPC 編碼、解碼,協(xié)議的定義“---Invocation.java Method.java

其實,這里并不是那種實用的開源RPC框架如Thrift中所指的編碼、IDL……上面兩個類只是RPC實現(xiàn)過程中輔助完成Java動態(tài)代理的實現(xiàn),說白了就是封裝客戶端需要調用的方法,然后指定生成的代理對象需要實現(xiàn)的接口(服務).

八,總結:

先運行MainServer.java啟動服務器,然后,再運行MainClient.java 啟動一個客戶端連接服務器就可以看到執(zhí)行結果。

當需要添加新的服務時:按以下步驟即可:①定義服務接口及其實現(xiàn)類,如:AnOtherEchoService.java ②:在MainServer.java中注冊新添加的服務。

③:在MainClient.java中編寫獲得新服務的代理對象的代碼,并用該代理對象調用新服務接口中聲明的方法。

這樣,在客戶端就能夠遠程地調用服務器上的一個新服務了。

整個源碼下載

參考文章:自定義的RPC的Java實現(xiàn)


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 墨竹工卡县| 徐汇区| 城市| 德化县| 安图县| 文安县| 莱州市| 新干县| 南投县| 孝昌县| 水富县| 巴林左旗| 米林县| 班戈县| 同德县| 柏乡县| 班玛县| 儋州市| 扶风县| 兴宁市| 当涂县| 五峰| 宁化县| 寿宁县| 蒙自县| 科尔| 松溪县| 来凤县| 高陵县| 百色市| 长岭县| 平昌县| 资中县| 兴安县| 共和县| 通州市| 达州市| 冀州市| 昌图县| 阳谷县| 南木林县|