最近公司開始要把各服務器的通訊方法改為Thrift,開始試著利用Thrift實作Node.js的通訊架構。
Apache Thrift 是 Facebook 實現的一種高效的、支持多種編程語言的遠程服務調用的框架,傳輸數據採用二進制格式,相對 XML 和 JSON 體積更小,對於高並發、大數據量和多語言的環境更有優勢。
講白了就是資料傳輸速度比傳統的Rest架構快非常多。可參考這篇Microservice Showdown – REST vs SOAP vs Apache Thrift (And Why It Matters)
這玩意很單純的利用tcp去傳輸資料,當然使省去很多瑣碎的多餘資料,省資源,效率更高,很適合用在整合散部各處的node.js微服物器。
但是Thrift也不是沒有缺點,省去了很多瑣碎的手續,相對資料的安全性就會降低,首先一大問題就是tcp連線可能隨時會中斷,所以要你要想要作長連線,最好還是乖乖地使用標準的socket協定,基本上還是建議使用在網路比較穩定的區網內服務通訊。
如果你有一個大型服務,需要收集(或請求)分散各處的微型服務,而這些服務都在同一個主機或是穩定的網路區域,就很適合利用Thrift作為通訊架構。
以下實作利用Thrift再在Node.js服務器中通訊的方法
首先建立一個nodejs專案,然後安裝thrift掛件
建立一個介面定義檔
Thrift其實最棒的地方就是支援很多語言,你只要寫一個.thrift定義檔,就可以幫你輸出各種語言的介面函示庫,c、java、php、ruby、python、js等等主流語言。
先做一個基本的定義檔 demoApi.thrift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| /** * 介面定義檔 */ /* 各語言命名空間 這邊不用改*/ namespace cpp demoApi namespace d demoApi namespace java demoApi namespace php demoApi namespace perl demoApi namespace haxe demoApi /* 定義呼叫的方法介面與參數 */ service DemoApi { void apiCall() /* 定義一個apiCall方法 */ }
|
這裡定義一個 DemoApi 的服務,這個服務裡面有一個apiCall方法,apiCall沒有接受參數,也沒有回傳值,所以前面加個viod。
- 編譯介面檔
demoApi.thrift 只是一個,定義文件,必須透過編譯器再編譯成各種語言的介面函示庫。
首先下載編譯器
下載後編譯你的demoApi.thrift定義檔,語法如下
1
| thrift --gen <language> <Thrift filename>
|
以下針對node.js作編譯
1
| hrift -r --gen js:node demoApi.thrift
|
編譯完成後產生介面函示庫gen-nodejs資料夾,這個就是node.js的介面函示庫。目前裡面應該會有兩隻檔案
DemoApi.js 方法介面的定義
demoApi_types.js 結構的定義(目前我們沒有定義任何結構)
4.開始撰寫nodejs各通訊端點
介面函示庫好了後就可以開始使用了,首先寫Server端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| var thrift = require("thrift"); * 匯入 thrift產生的函式庫 */ var DemoApi = require("./gen-nodejs/DemoApi"); * 定義一個 DemoApi 服務器 */ var server = thrift.createServer(DemoApi, { * 最單純的通訊,呼叫定義的apiCall方法,定義的方法請參照demo.thrift */ apiCall: function (result) { console.log("apiCall()"); result(null); } }); * 偵聽8080 port */ server.listen(8080);
|
再來是Client端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| var thrift = require("thrift"); * 匯入 thrift產生的函式庫 */ var DemoApi = require("./gen-nodejs/DemoApi"); * 通訊協定定義 */ var transport = thrift.TBufferedTransport(); var protocol = thrift.TBinaryProtocol(); * 連線 */ var connection = thrift.createConnection("localhost", 8080, { transport: transport, protocol: protocol }); * 連線錯誤事件 */ connection.on('error', function (err) { console.error(err); }); * 打開一個DemoApi服務通訊流程 */ var client = thrift.createClient(DemoApi, connection); * 最單純的通訊,呼叫定義的apiCall方法,定義的方法請參照demo.thrift */ client.apiCall(function (err, response) { console.log('apiCall()'); connection.end(); });
|
好了之後你可以分別執行server與client,順利的話應該可以看到執行的console.log結果
接下來我們試著傳送一些資料,目前Thrift可以定義的類型如下
- bool
- i8 (byte)
- i16(16位元整數)
- i32(32位元整數)
- i64(64位元整數)
- double(64位元浮點數)
- string(字串)
- binary(byte array)
我們在定義檔demoApi.thrift上加上一個新的方法
1 2 3 4
| service DemoApi { void apiCall(), /* 逗號隔開 */ i32 add(1:i32 num1, 2:i32 num2) /* 新增add這個方法 */
|
add這個方法傳入兩個32位元整數,最前面的i32代表回傳的型態
加上後記得再編譯一次
1
| hrift -r --gen js:node demoApi.thrift
|
Servre端
1 2 3 4 5 6 7 8
| apiCall: function (result) { console.log("apiCall()"); result(null); }, add: function (n1, n2, result) { console.log("add(", n1, ",", n2, ")"); result(null, n1 + n2); }
|
Client端
1 2 3
| client.add(1, 1, function (err, response) { console.log("1+1=" + response); });
|
這個方法是把傳入的兩個數字相加後再return回去。
再次執行後應該可以看到對應的結果!
其他資料型態與完整範例請至我的gitlab
https://gitlab.com/cainplay/Thrift_demo