設計協議
相對于 HTTP 的用處,RPC 更多的是負責應用間的通信,所以性能要求相對更高,但 HTTP 協議的資料包大小相對請求資料本身要大很多,又需要加入很多無用的內容,比如換行符號、回車符等;
還有一個更重要的原因是,HTTP 協議屬于無狀態協議,客戶端無法對請求和回應進行關聯,每次請求都需要重新建立連接,回應完成后再關閉連接,因此,對于要求高性能的 RPC 來說,HTTP 協議基本很難滿足需求,所以
RPC 會選擇設計更緊湊的私有協議,
協議要點:
在協議頭里面,我們除了會放協議長度、序列化方式,還會放一些像協議標示、訊息 ID、訊息型別這樣的引數,而協議體一般只放請求介面方法、請求的業務引數值和一些擴展屬性,這樣一個完整的 RPC 協議大概就出來了,協議頭是由一堆固定的長度引陣列成,而協議體是根據請求介面和引數構造的,長度屬于可變的,具體協議如下圖所示:

可擴展:
如果加新引數的話,首先不可以放在協議頭,那么放在協議體中可以嗎
答案是不可以的
因為協議體中的內容都是要經過序列和反序列化的,如果要獲取到你引數的值,就必須把整個協議體里面的資料經過反序列化出來,在某些場景下這樣做代價有點高
那就變成協議頭(固定部分,擴展部分),協議體

序列化和反序列化
實際上任何一種序列化框架,核心思想就是設計一種序列化協議,將物件的型別、屬性型別、屬性值一一按照固定的格式寫到二進制位元組流中來完成序列化,再按照固定的格式一一讀出物件的型別、屬性型別、屬性值,通過這些資訊重新創建出一個新的物件,來完成反序列化
常見的序列化方式有:JDK,Json,Hessian,Protobuf序列化方式,
選擇序列化方式要考慮的因素
-
性能和效率
-
空間開銷‘
-
通用性和兼容性
-
安全性

首選的還是 Hessian 與 Protobuf,因為他們在性能、時間開銷、空間開銷、通用性、兼容性和安全性上,都滿足了我們的要求,其中 Hessian 在使用上更加方便,在物件的兼容性上更好;Protobuf 則更加高效,通用性上更有優勢,
在使用程序中編碼需注意那些問題
1.物件要盡量簡單,沒有太多的依賴關系,屬性不要太多,盡量高內聚;
2. 入參物件與回傳值物件體積不要太大,更不要傳太大的集合;
3. 盡量使用簡單的、常用的、開發語言原生的物件,尤其是集合類;
4. 物件不要有復雜的繼承關系,最好不要有父子類的情況
IO
零拷貝
取消用戶空間和內核空間的拷貝
應用行程的每一次讀寫可以直接將資料從內核區中取出或者寫入,再通過DMA將內核中的資料拷貝到網卡或從網卡拷貝到內核,
所以解決方式就是使用戶空間和內核空間的資料都在一個地方存取,那就是虛擬記憶體,
RPC協議在傳輸資料程序中,中間可能會拆分成好幾個資料包,也可能會合并其他請求的資料包,所以訊息都需要有邊界,那么一端的機器收到訊息之后,就需要對資料包進行處理,根據邊界對資料包進行分割和合并,最侄訓得一條完整的訊息,
收到訊息后,對資料包的分割和合并,是在用戶空間完成,還是在內核空間完成的
Netty 的零拷貝就是為了解決這個問題,在用戶空間對資料操作進行優化,
Netty 框架中很多內部的 ChannelHandler 實作類,都是通過 CompositeByteBuf、slice、wrap 操作來處理 TCP 傳輸中的拆包與粘包問題的,
那么 Netty 解決用戶空間與內核空間之間的資料拷貝問題的方法:
Netty 的 ByteBuffer 可以采用 Direct Buffers,使用堆外直接記憶體進行 Socketd 的讀寫操作,最終的效果與虛擬記憶體所實作的效果是一樣的,
SPI
設計RPC框架時,為了實作插件化架構,可將功能點抽象成介面,將介面與實作分離,并提供介面的默認實作
利用SPI(服務發現機制),可動態的為介面尋找服務
實作:在Classpath 下的 METAINF/services 目錄里創建一個以服務介面命名的檔案,這個檔案里的內容就是這個介面的具體實作類,
服務發現
還是最終一致性性能更好
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/499518.html
標籤:Java
上一篇:面試記錄
