作者:_Yasin
blog.csdn.net/u013592964/article/details/80441205
1. RPC框架的概念
RPC(Remote Procedure Call)–遠程程序呼叫,通過網路通信呼叫不同的服務,共同支撐一個軟體系統,微服務實作的基石技術,
使用RPC可以解耦系統,方便維護,同時增加系統處理請求的能力,

上面是一個簡單的軟體系統結構,我們拆分出來用戶系統和訂單系統做為服務存在,讓不同的站點去呼叫,
只需要引入各個服務的介面包,在代碼中呼叫RPC服務就跟呼叫本地方法一樣,我剛接觸到這種呼叫方式的時候頗為驚奇,我明明呼叫的就是java語言方法啊(已java為例,現在RPC框架一般都支持多語言),怎么就呼叫了遠程的服務了呢??
2. RPC框架的原理決議
最近自己寫了一個簡單的RPC框架KRPC,本文原理分析結合中代碼,均為該框架原始碼,RPC與RMI的區別看這篇文章《Java RMI 和 RPC 的區別》,
2.1 流程縱覽

如上圖所示,我將一個RPC呼叫流程概括為上圖中5個流程,左邊3個為客戶端流程,右邊兩個為服務端流程,
下面就各流程進行決議
2.2 客戶端呼叫
服務呼叫方在呼叫服務時,一般進行相關初始化,通過組態檔/配置中心 獲取服務端地址用戶呼叫,
// 用戶服務介面
public interface UserService {
public User genericUser(Integer id,String name,Long phone);
}
//呼叫方
//服務初始化
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呼叫方法肯定就有疑惑,它不是一個介面嗎,直接呼叫應該沒啥效果啊,我也沒有引入實作包,
帶著這個疑惑,我們就進入下一個知識點,動態代理,

2.3 動態代理
動態代理這東西意如其名,它代理你幫你做事情,動態代理看這篇文章《詳解 Java 中的三種代理模式》,
上面我們不說道直接呼叫一個介面中的方法,并且沒有用該介面的實作類呼叫,那么方法是怎么生效的呢?
可以看到這個用戶服務這個service是由ProxyFactory代理工程創造的,在該ProxyFactory#create()方法中就跟一個代理處理器系結在一起了,
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//構造請求request
Request request = new Request();
....
return RequestHandler.request(serviceName, request,returnClass);
}
這個類實作了InvocationHandler介面(JDK提供的動態代理技術),每次去呼叫介面方法,最終都交由該handler進行處理,
這個環節一般會獲取方法的一些資訊,例如方法名,方法引數型別,方法引數值,回傳物件型別,
同時這個環節會提供序列化功能,一般的RPC網路傳輸使用TCP(哪怕使用HTTP)傳輸,這里也要將這些引數進行封裝成我們定義的資料介面進行傳輸,
2.4 網路傳輸
我們通過將方法引數進行處理后,就要使用發起網路請求,使用tcp傳輸的就利用socket通信進行傳輸,這一塊我開源專案中使用的同步堵塞的方案進行請求,也可以使用一些非堵塞方案進行請求,效率會更高一些,
2.5 服務端資料接受
這一塊使用netty,可以快速一個高性能、高可靠的一個服務端,
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();
}
}
上面代碼是我專案中使用的服務端代碼,關于netty網上學習的資料很多,這里也只是宏觀的講解RPC原理,就不展開,
2.6 真實呼叫
服務端獲取客戶端請求的資料后, 呼叫請求中的方法,方法引數值,通過反射呼叫真實的方法,獲取其回傳值,將其序列化封裝,通過netty進行資料回傳,客戶端在接受資料并決議,這就完成了一次rpc請求呼叫的全程序,
method = clazz.getMethod(request.getMethodName(),requestParamTypes);
method.setAccessible(true);
result = method.invoke(service, requestParmsValues)
上面代碼片段為通過反射呼叫真實方法
2.7 服務端的動態加載
通過2.2到2.6的說明,一次RPC請求程序大致如此,但是一個RPC框架會有很多細節需要處理,
其實在一次請求呼叫前,服務端肯定要先啟動,
服務端作為一個容器,跟我們熟知的tomcat一樣,它可以動態的加載任何專案,所以在服務端啟動的時候,必須要進行一個動態加載的程序,
在KRPC中,我使用了URLClassLoader動態加載一個指定路徑的jar包,任何業務服務的實作所依賴的jar包都可以放入該路徑中,
3. 總結
一個RPC框架大致需要動態代理、序列化、網路請求、網路請求接受(netty實作)、動態加載、反射這些知識點,
現在開源及各公司自己造的RPC框架層出不窮,唯有掌握原理是一勞永逸的,
掌握原理最好的方法莫不是閱讀原始碼,自己動手寫是最快的,
近期熱文推薦:
1.Java 15 正式發布, 14 個新特性,重繪你的認知!!
2.終于靠開源專案弄到 IntelliJ IDEA 激活碼了,真香!
3.我用 Java 8 寫了一段邏輯,同事直呼看不懂,你試試看,,
4.吊打 Tomcat ,Undertow 性能很炸!!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/224443.html
標籤:其他
