摘要:報文類型對于框架來說,報文可能有多種類型心跳類型報文認證類型報文請求類型報文響應類型報文等。接口調用請求的發送,在多條連接之間進行負載均衡。
1 需求分析
RPC 全稱 Remote Procedure Call ,簡單地來說,它能讓使用者像調用本地方法一樣,調用遠程的接口,而不需要關注底層的具體細節。
例如車輛違章代辦功能,如果車輛因為某種原因違章,只需要通過這個違章代辦功能(它也許是個APP),我們就能動動手指,而省去了一些跑腿的工作。
不像微服務背景下大家所說的 RPC 框架,如 Dubbo 之類。這個 RPC 框架不提供過多的關于服務注冊、服務發現、服務管理等功能。它針對的是這樣的一些場景:在內部網絡,或者局域網內,兩個屬于同個業務的系統之間需要通信,而我們又覺得去設計多一種二進制網絡協議過于繁瑣并且沒有必要,這時候如果給客戶端開發者一些明確的接口,讓他知道實現什么功能該調用什么接口,那么省去的工作量以及開發效率上的提升不言而喻。
這個 RPC 系統基于 Java 語言實現,需求如下:
RPC 服務端可以通過一條長連接發布多個接口(Interface),客戶端按需生成對應接口的代理。
RPC 客戶端也可以發布接口,以便在必要的時候,服務端可以主動調用客戶端的接口實現
客戶端與服務端之間保持長連接并且維持心跳
服務端針對不同的接口實現,可以指定不同的線程池去處理
序列化協議支持擴展
通信協議與具體編程語言無關
支持并發調用,一個RPC客戶端實例要求是線程安全的
2. 通信協議設計高效的通信協議一般是二進制格式的,比較常見的還有文本協議比如說HTTP,為了追求效率,這個 RPC 框架就采用二進制格式。
協議的基本要素 魔數要了解到,報文是在網絡上傳輸的,安全性比較低,因此有必要采取一些措施使得并不是任何人都可以隨隨便便往我們的端口上發東西,因此我們對報文要有一個初步的識別功能,這時候“魔數(magic number)”就派上用場了。魔數并不受任何規范約束,沒有人可以要求你的魔數應該遵循什么規范,實際上魔數只是我們通信雙方都約定的一個“暗號”,不知道這個暗號的人就無法參與進通信中。例如 Java 源文件編譯后的 class 文件開頭就有一個魔數:0xCAFEBABE,隨隨便便打開一個class文件用十六進制編輯器查看,就能看到。
Java 虛擬機加載 class 的時候會先驗證魔數。如果不是 CAFEBABE 就認為是不合法的 class 文件,并拒絕加載。
不過魔數起到的安全防范作用是非常有限的,“有心人”可以通過抓取網絡包就識別出魔數了。因此魔數這個東西其實是“防君子不防小人”。
協議版本一個協議可能也會有多個版本,例如說 HTTP1.0 和 HTTP1.1,不同版本的協議元素可能發生了改變,解析方式也會發生改變,因此協議設計這一塊,需要預留出地方聲明協議的版本,通信雙方在解析協議或者拼裝協議的時候才有跡可循。
報文類型對于RPC框架來說,報文可能有多種類型:心跳類型報文、認證類型報文、請求類型報文、響應類型報文等。
上下文 IDRPC 調用其實是一個“請求-響應”的過程,并且跨物理機器,因此每次請求和響應,都必須帶上上下文 ID,通信雙方才能把請求和響應對應起來。
狀態狀態用來標識一次調用時正常結束還是異常結束,通常由被調用方置狀態。
請求數據即發送到服務端的調用請求,通常是序列化后的二進制流,長度不定。
長度編碼字段收報文的一方怎么知道發報文的那一方發了多少字節呢?因此發送方必須在協議里告訴接收方需要接受多少字節才算一個完整的報文。
保留字段協議一旦被設計,并非一成不變的,日后可能有變動的可能,因此還需要考慮保留一些字節空間作為保留字段,以備日后協議的擴展。
協議設計結合以上的一些設計原則,具體協議設計如下:
------------------------------------------------------------------------ | magic (2bytes) | version (1byte) | type (1byte) | reserved (7bits) | ------------------------------------------------------------------------ | status (1byte) | id (8bytes) | body length (4bytes) | ------------------------------------------------------------------------ | | | body ($body_length bytes) | | | ------------------------------------------------------------------------3. 鏈路可靠性
客戶端與服務端之間的連接采用 TCP 長連接,一個客戶端與服務端之間保持至少一條長連接。接口調用請求的發送,在多條連接之間進行負載均衡。
每條連接在空閑的時候,由客戶端主動向服務端發送心跳報文,并且客戶端在發現連接失效或斷開的時候,自動進行重連。
每個客戶端向服務端建立連接后,在正式發起接口調用請求之前,都需要進行check in 操作, check in 操作主要是將客戶端的身份標識(identifier)和客戶端的心跳間隔告訴服務端。利用 netty 的 handler 責任鏈機制和自帶的 IdleStateHandler,自動檢測出連接是否空閑,并在空閑時觸發心跳報文的發送。而服務端在客戶端 checkin 后,根據客戶端的心跳頻率,在自己的 handler pipeline 上動態加入一個 IdleStateHandler,來檢測出客戶端是否已經失聯,如果是,則主動關閉連接。
同時,客戶端本地將會起一個定時執行任務的線程,定期檢查連接是否失效,如果失效,則關閉舊連接,并進行連接的重建。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72040.html
摘要:概述在簡易框架需求與設計這篇文章中已經給出了協議的具體細節,協議類型為二進制協議,如下協議的解碼我們稱為,編碼我們成為,下文我們將直接使用和術語。直接貼代碼,參考前文提到的協議格式閱讀以下代碼協議編碼器 概述 在《簡易RPC框架:需求與設計》這篇文章中已經給出了協議的具體細節,協議類型為二進制協議,如下: ---------------------------------------...
摘要:由于我們還未談到具體的調用機制,因此暫且認為就是把一個包含了調用信息的對象,從經過序列化,變成一串二進制流,發送到了端。 概述 在上一篇文章《簡易RPC框架:基于 netty 的協議編解碼》中談到對于協議的 decode 和 encode,在談 decode 之前,必須先要知道 encode 的過程是什么,它把什么東西轉化成了二進制協議。由于我們還未談到具體的 RPC 調用機制,因此暫...
摘要:服務提供者在啟動時,向注冊中心注冊自己提供的服務。注冊中心返回服務提供者地址列表給消費者,如果有變更,注冊中心將基于長連接推送變更數據給消費者。 先來了解一下這些年架構的變化,下面的故事是我編的。。。。 傳統架構:很多年前,剛學完JavaWeb開發的我憑借一人之力就開發了一個網站,網站 所有的功能和應用都集中在一起,方便了我的開發同時也節省了成本。但是后來我的網站訪問流量突然加大,我通...
摘要:的服務治理平臺發源于早期的個人項目。客戶端發現模式要求客戶端負責查詢注冊中心,獲取服務提供者的列表信息,使用負載均衡算法選擇一個合適的服務提供者,發起接口調用請求。系統和系統之間,少不了數據的互聯互通。隨著微服務的流行,一個系統內的不同應用進行互聯互通也是常態。 PowerDotNet的服務治理平臺發源于早期的個人項目Power.Apix。這個項目借鑒了工作過的公司的服務治理方案,站在...
閱讀 3044·2021-11-22 09:34
閱讀 3636·2021-08-31 09:45
閱讀 3836·2019-08-30 13:57
閱讀 1670·2019-08-29 15:11
閱讀 1681·2019-08-28 18:04
閱讀 3218·2019-08-28 17:59
閱讀 1558·2019-08-26 13:35
閱讀 2188·2019-08-26 10:12