1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        一文了解RPC框架原理

        共 3042字,需瀏覽 7分鐘

         ·

        2020-11-24 22:06

        點(diǎn)擊上方“開源Linux”,選擇“設(shè)為星標(biāo)”

        回復(fù)“學(xué)習(xí)”獲取獨(dú)家整理的學(xué)習(xí)資料!

        1.RPC框架的概念

        RPC(Remote Procedure Call)–遠(yuǎn)程過程調(diào)用,通過網(wǎng)絡(luò)通信調(diào)用不同的服務(wù),共同支撐一個(gè)軟件系統(tǒng),微服務(wù)實(shí)現(xiàn)的基石技術(shù)。使用RPC可以解耦系統(tǒng),方便維護(hù),同時(shí)增加系統(tǒng)處理請求的能力。

        上面是一個(gè)簡單的軟件系統(tǒng)結(jié)構(gòu),我們拆分出來用戶系統(tǒng)和訂單系統(tǒng)做為服務(wù)存在,讓不同的站點(diǎn)去調(diào)用。

        只需要引入各個(gè)服務(wù)的接口包,在代碼中調(diào)用RPC服務(wù)就跟調(diào)用本地方法一樣,我剛接觸到這種調(diào)用方式的時(shí)候頗為驚奇,我明明調(diào)用的就是java語言方法?。ㄒ詊ava為例,現(xiàn)在RPC框架一般都支持多語言),怎么就調(diào)用了遠(yuǎn)程的服務(wù)了呢??

        2.RPC框架的原理解析

        2.1 流程縱覽

        如上圖所示,我將一個(gè)RPC調(diào)用流程概括為上圖中5個(gè)流程,左邊3個(gè)為客戶端流程,右邊兩個(gè)為服務(wù)端流程。下面就各流程進(jìn)行解析

        2.2 客戶端調(diào)用

        服務(wù)調(diào)用方在調(diào)用服務(wù)時(shí),一般進(jìn)行相關(guān)初始化,通過配置文件/配置中心 獲取服務(wù)端地址 用戶調(diào)用:

        //?用戶服務(wù)接口
        public?interface?UserService?{
        ?
        ?public?User?genericUser(Integer?id,String?name,Long?phone);
        ?
        }


        //調(diào)用方
        //服務(wù)初始化
        KRPC.init("D:\\krpc\\service\\demo\\conf\\client.xml");
        UserService?service?=?ProxyFactory.create(UserService.class,?"demo","demoService");
        User?user?=?service.genericUser(1,?"yasin",?1888888888L);

        一開始接觸RPC調(diào)用方法肯定就有疑惑,它不是一個(gè)接口嗎,直接調(diào)用應(yīng)該沒啥效果啊,我也沒有引入實(shí)現(xiàn)包。

        帶著這個(gè)疑惑,我們就進(jìn)入下一個(gè)知識點(diǎn),動(dòng)態(tài)代理

        2.3動(dòng)態(tài)代理

        動(dòng)態(tài)代理這東西意如其名,它代理你幫你做事情。上面我們不說直接調(diào)用一個(gè)接口中的方法,并且沒有用該接口的實(shí)現(xiàn)類調(diào)用,那么方法是怎么生效的呢?

        可以看到這個(gè)用戶服務(wù)這個(gè)service是由ProxyFactory代理工程創(chuàng)造的,在該P(yáng)roxyFactory#create()方法中就跟一個(gè)代理處理器綁定在一起了

        @Override
        public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{

        ????//構(gòu)造請求request
        ????Request?request?=?new?Request();
        ????....

        ???return?RequestHandler.request(serviceName,?request,returnClass);
        }

        這個(gè)類實(shí)現(xiàn)了InvocationHandler接口(JDK提供的動(dòng)態(tài)代理技術(shù)),每次去調(diào)用接口方法,最終都交由該handler進(jìn)行處理。這個(gè)環(huán)節(jié)一般會獲取方法的一些信息,例如方法名,方法參數(shù)類型,方法參數(shù)值,返回對象類型。

        同時(shí)這個(gè)環(huán)節(jié)會提供序列化功能,一般的RPC網(wǎng)絡(luò)傳輸使用TCP(哪怕使用HTTP)傳輸,這里也要將這些參數(shù)進(jìn)行封裝成我們定義的數(shù)據(jù)接口進(jìn)行傳輸。

        2.4網(wǎng)絡(luò)傳輸

        我們通過將方法參數(shù)進(jìn)行處理后,就要使用發(fā)起網(wǎng)絡(luò)請求,使用tcp傳輸?shù)木屠胹ocket通信進(jìn)行傳輸,這一塊我開源項(xiàng)目中使用的同步堵塞的方案進(jìn)行請求,也可以使用一些非堵塞方案進(jìn)行請求,效率會更高一些。

        2.5服務(wù)端數(shù)據(jù)接受

        這一塊使用netty,可以快速一個(gè)高性能、高可靠的一個(gè)服務(wù)端。

        public?class?ServerHandler?extends?ChannelInboundHandlerAdapter?{

        ????@Override
        ????public?void?channelRead(ChannelHandlerContext?ctx,?Object?msg)?throws?Exception?{

        ????????ByteBuf?buf?=?(ByteBuf)msg;??
        ????????byte[]?bytes?=?new?byte[buf.readableBytes()];??
        ????????buf.readBytes(bytes);??

        ????????byte[]?responseBytes?=?RequestHandler.handler(bytes);
        ????????ByteBuf?resbuf?=?ctx.alloc().buffer(responseBytes.length);
        ????????resbuf.writeBytes(responseBytes);
        ????????ctx.writeAndFlush(resbuf);

        ????}

        ????@Override
        ????public?void?exceptionCaught(ChannelHandlerContext?ctx,?Throwable?cause)?throws?Exception?{
        ????????cause.printStackTrace();
        ????????ctx.close();
        ????}

        }

        上面代碼是我項(xiàng)目中使用的服務(wù)端代碼。關(guān)于netty網(wǎng)上學(xué)習(xí)的資料很多,這里也只是宏觀的講解RPC原理,就不展開。

        2.6真實(shí)調(diào)用

        服務(wù)端獲取客戶端請求的數(shù)據(jù)后, 調(diào)用請求中的方法,方法參數(shù)值,通過反射調(diào)用真實(shí)的方法,獲取其返回值,將其序列化封裝,通過netty進(jìn)行數(shù)據(jù)返回,客戶端在接受數(shù)據(jù)并解析,這就完成了一次rpc請求調(diào)用的全過程。

        method?=?clazz.getMethod(request.getMethodName(),requestParamTypes);
        method.setAccessible(true);
        result?=?method.invoke(service,?requestParmsValues)

        上面代碼片段為通過反射調(diào)用真實(shí)方法

        2.7 服務(wù)端的動(dòng)態(tài)加載

        通過2.2到2.6的說明,一次RPC請求過程大致如此,但是一個(gè)RPC框架會有很多細(xì)節(jié)需要處理。

        其實(shí)在一次請求調(diào)用前,服務(wù)端肯定要先啟動(dòng)。

        服務(wù)端作為一個(gè)容器,跟我們熟知的tomcat一樣,它可以動(dòng)態(tài)的加載任何項(xiàng)目。所以在服務(wù)端啟動(dòng)的時(shí)候,必須要進(jìn)行一個(gè)動(dòng)態(tài)加載的過程。在KRPC中,我使用了URLClassLoader動(dòng)態(tài)加載一個(gè)指定路徑的jar包,任何業(yè)務(wù)服務(wù)的實(shí)現(xiàn)所依賴的jar包都可以放入該路徑中。

        3.總結(jié)

        一個(gè)RPC框架大致需要?jiǎng)討B(tài)代理、序列化、網(wǎng)絡(luò)請求、網(wǎng)絡(luò)請求接受(netty實(shí)現(xiàn))、動(dòng)態(tài)加載、反射這些知識點(diǎn)?,F(xiàn)在開源及各公司自己造的RPC框架層出不窮,唯有掌握原理是一勞永逸的。掌握原理最好的方法莫不是閱讀源碼,自己動(dòng)手寫是最快的。

        作者:_Yasin

        出處:http://002ii.cn/rxIivD

        - End -

        關(guān)注「開源Linux」加星標(biāo),提升IT技能

        瀏覽 47
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            久久网逼| 欧美精品国产动漫 | 黄色操逼逼 | 俺去也天天幹 | 日韩中字无码 | 女人爽到高潮视频免费视频 | 狠狠干很很操 | 日韩无码视频免费 | 国产精品久久7777777精品无码 | 91无码一区二区三区在线 |