在好久以前的單機時代,一臺電腦中跑着多個進程,進程之間沒有交流各幹各的,就這樣過了不少年。忽然有一天有了新需求,A進程須要實現一個畫圖的功能,剛好鄰居B進程已經有了這個功能,偷懶的程序員C想出了一個辦法:A進程調B進程的畫圖功能。因而出現了IPC(Inter-process communication,進程間通訊)。就這樣程序員C愉快的去吃早餐去了!java
又過了幾年,到了互聯網時代,每一個電腦都實現了互聯互通。這時候僱主又有了新需求,當時還沒掛的A進程須要實現使用tensorflow識別出笑臉 >_< 。說巧不巧,遠在幾千裏的一臺快速運行的電腦上已經實現了這個功能,睡眼惺忪的程序媛D接手了這個A進程後借鑑以前IPC的實現,把IPC擴展到了互聯網上,這就是RPC(Remote Procedure Call,遠程過程調用)。RPC其實就是一臺電腦上的進程調用另一臺電腦上的進程的工具。成熟的RPC方案大多數會具有服務註冊、服務發現、熔斷降級和限流等機制。目前市面上的RPC已經有不少成熟的了,好比Facebook家的Thrift、Google家的gRPC、阿里家的Dubbo和螞蟻家的SOFA。git
接口定義語言,簡稱IDL,是實現端對端之間可靠通信的一套編碼方案。這裏有涉及到傳輸數據的序列化和反序列化,咱們經常使用的http的請求通常用json當作序列化工具,定製rpc協議的時候由於要求響應迅速等特色,因此大多數會定義一套序列化協議。好比:程序員
Protobuf:github
講到Protobuf就得講到該庫做者的另外一個做品Cap'n proto了,號稱性能是直接秒殺Google Protobuf,直接上官方對比:json
雖然知道不少比Protobuf更快的編碼方案,可是快到這種地步也是厲害了,爲啥這麼快,Cap’n Proto的文檔裏面就馬上說明了,由於Cap'n Proto沒有任何序列號和反序列化步驟,Cap'n Proto編碼的數據格式跟在內存裏面的佈局是一致的,因此能夠直接將編碼好的structure直接字節存放到硬盤上面。貼個栗子:數組
咱們這裏要定製的編碼方案就是基於protobuf和Cap'n Proto結合的相似的語法。由於本人比較喜歡刀劍神域裏的男主角,因此就給這個庫起了個名字—— Kiritobuf。網絡
首先咱們定義kirito的語法:架構
=號左邊是參數名,右邊是參數類型框架
參數類型:函數
UInt8, UInt16, UInt32, UInt64 - Floating-point: Float32, Float64 - Blobs: Text, Data - Lists: List(T)
定義好了語法和參數類型,咱們先過一下生成有抽象關係代碼的流程:
取到.kirito後綴的文件,讀取所有字符,經過詞法分析器生成token,獲得的token傳入語法分析器生成AST (抽象語法樹)。
首先咱們新建一個kirito.js文件:
定義好了一些必要的字面量,接下來首先是詞法分析階段。
一、詞法解析
咱們設計詞法分析獲得的Token是這樣子的:
詞法分析步驟:
代碼以下:
二、語法分析
獲得上面的詞法分析的token後,咱們就能夠對該token作語法分析,咱們須要最終生成的AST的格式以下:
看上圖咱們能友好的獲得結構、參數、數據類型、函數之間的依賴和關係,步驟:
一、遍歷詞法分析獲得的token數組,經過調用分析函數提取token之間的依賴節點
二、分析函數內部定義token提取規則,好比:
三、遞歸調用分析函數提取對應節點依賴關係,將節點添加到AST中
代碼以下:
三、轉換器
獲得了語法分析的AST後咱們須要進一步對AST轉換爲更易操做的js對象。格式以下:
經過上面這個格式,咱們能夠更容易的知道有幾個service、service裏有多少個函數以及函數的參數。
代碼以下:
RPC協議有多種,能夠是json、xml、http2,相對於http1.x這種文本協議,http2.0這種二進制協議更適合做爲RPC的應用層通訊協議。不少成熟的RPC框架通常都會定製本身的協議已知足各類變化莫測的需求。
好比Thrift的TBinaryProtocol、TCompactProto-col等,用戶能夠自主選擇適合本身的傳輸協議。
(除了按字節編址還有按字編址和按位編址),咱們這裏只討論字節編址。每一個機器由於不一樣的系統或者不一樣的CPU對內存地址的編碼有不同的規則,通常分爲兩種字節序:大端序和小端序。
舉個栗子:
好比一個整數:258,用16進製表示爲0x0102,咱們把它分爲兩個字節0x01和ox02,對應的二進制爲0000 0001和0000 0010。在大端序的電腦上存放形式以下:
小端序則相反。爲了保證在不一樣機器之間傳輸的數據是同樣的,開發一個通信協議時會首先約定好使用一種做爲通信方案。java虛擬機採用的是大端序。在機器上咱們稱爲主機字節序,網絡傳輸時咱們稱爲網絡字節序。網絡字節序是TCP/IP中規定好的一種數據表示格式,它與具體的CPU類型、操做系統等無關,從而能夠保證數據在不一樣主機之間傳輸時可以被正確解釋。網絡字節序採用大端排序方式。
咱們這裏就不造新應用層協議的輪子了,咱們直接使用MQTT協議做爲咱們的默認應用層協議。MQTT(Message Queuing Telemetry Tran-sport,消息隊列遙測傳輸協議),是一種基於發佈/訂閱(publish/subscribe)模式的「輕量級」通信協議,採用大端序的網絡字節序傳輸,該協議構建於TCP/IP協議上。
先貼下實現完的代碼調用流程,首先是server端:
client端:
不管是server端定義函數或者client端調用函數都是比較簡潔的步驟。接下來咱們慢慢剖析具體的邏輯實現。
貼下具體的調用流程架構圖:
調用流程總結:
說完了調用流程,如今開始講解具體的實現。
server:
定義protocol接口,加上這一層是爲了之後的多協議,mqtt只是默認使用的協議:
接下來是server端的暴露出去的接口:
client:
定義protocol接口:
最後是client端暴露的接口:
就這樣,一個簡單的IDL+RPC框架就這樣搭建完成了。這裏只是描述RPC的原理和經常使用的調用方式,要想用在企業級的開發上,還得加上服務發現、註冊,服務熔斷,服務降級等,讀者若是有興趣能夠在Github上fork下來或者提PR來改進這個框架,有什麼問題也能夠提Issue, 固然PR是最好的 : ) 。
倉庫地址:
RPC: https://github.com/polixjs/po...
IDL:https://github.com/rickyes/ki...
更多文章請訪問數瀾社區,歡迎你們來一塊兒學習~