標簽: node.js zookeeper thrift 微服務
當項目越來越大的時候,必然會做到服務化,而且大型項目下,極有可能調用跨語言服務。所以我寫了這個通用的框架,用于發布/調用node服務, node-thrift-service 。
Thrift
Apache Thrift 是一款跨語言的服務框架,傳輸數據采用二進制格式,相對 XML 和 JSON 體積更小,對于高并發、大數據量和多語言的環境更有優勢。

安裝
在 Thrift 官網上下載相應的版本,按步驟一步一步安裝即可。
類型
數據類型
Thrift 支持 8 種數據類型:
- bool: true or false
- byte: signed byte
- i16/i32/i64: 16/32/64位 signed integer
- double: 64位
- binary: byte array
- string
3 種容器:
- list<t1>: 排序數組,可以重復
- set<t1>: 集合,每個元素唯一
- map<t1, t2>: t1 唯一
命名空間
支持命名空間(Namespaces)// 如 Java 的包名,C++ 的 namespaces
namespace cpp com.hello
namespace java com.hello
引用
可以引用其他 thrift 文件
include "hello.thrift"
...
常量
const i32 GOOD = 1024;
const list<string> ABC = ["a", "b", "c"];
數據結構
struct Animal {
1: required double age;
2: required double weight;
}
struct Cat {
1: required string name;
2: required string food;
3: required Animal common;
8: optional bool curl;
}
接口
Thrift 通過語法規范定義接口,形式類似于偽代碼,例如:
exception TypeError {
1: required string err
2: optional string message
}
service Hello {
string say(1: string name);
Cat getCat(1: string name) throws (1: TypeError err)
}
更詳細的教程可以看這里。
生成代碼
上述代碼定義了一個 Hello
服務,將其保存為 hello.thrift
。
使用 thrift
命令可以生成代碼。
thrift --gen <language> <Thrift filename>
// nodejs
// ls ./gen-nodejs => Hello.js hello_types.js
thrift --gen js:node hello.thrift
// java
// ls ./gen-java => Animal.java Cat.java Hello.java TypeError.java
thrift --gen java hello.thrift
使用
示例
'use strict';
const thrift = require('thrift');
const Hello = require('./gen-nodejs/Hello'),
types = require('./gen-nodejs/hello_types');
...
let server = thrift.createServer(Hello, {
say(name, callback){
callback(null, 'Hello ' + name);
}
...
}, {});
server.listen(7800);
server.on('error', console.error);
server.on('listening', () => {
let conn = thrift.createConnection('127.0.0.1', 7800);
let client = thrift.createClient(Hello, conn);
client.say('Thrift', console.log);
// null 'Hello Thrift'
});
API
// processor 即生成的 ./gen-nodejs/Hello.js
// handler 為接口的實現,參數在 hello.thrift 定義的基礎上多一個 callback(error, result)
// options 可以定義 傳輸協議 以及 tls
// var transport = (options && options.transport) ? options.transport : TBufferedTransport;
// var protocol = (options && options.protocol) ? options.protocol : TBinaryProtocol;
let server = thrift.createServer(processor, handler, options)
// 這個就一目了然,options 與 server 創建時的 options 用法一致
let connection = thrift.createConnection(host, port, options)
let client = thrift.createClient(processor, connection)
ZooKeeper
ZooKeeper 是一個分布式的,開放源碼的分布式應用程序協調服務,是 Google 的 Chubby 一個開源的實現,是 Hadoop 和 Hbase 的重要組件。它是一個為分布式應用提供一致性服務的軟件,提供的功能包括:配置維護、域名服務、分布式同步、組服務等。
ZooKeeper 的目標就是封裝好復雜易出錯的關鍵服務,將簡單易用的接口和性能高效、功能穩定的系統提供給用戶。
ZooKeeper 包含一個簡單的原語集,提供Java和C的接口。
ZooKeeper 代碼版本中,提供了分布式獨享鎖、選舉、隊列的接口,其中分布鎖和隊列有Java和C兩個版本,選舉只有Java版本。
詳細原理教程可以參見官網。
Node.js 的 ZooKeeper 版本為 C 接口的封裝。
使用
Zookeeper 提供一個類似于文件系統的服務。
Zookeeper 每一級節點分為永久節點和臨時節點,只有永久節點才可以創建子節點,類似于文件系統一級一級的『目錄』。
Zookeeper 的『節點』均可以存放數據,并且可以對節點的權限進行控制。

client 與 Zookeeper 服務器為 TCP 長連接,服務器為每個客戶端生成一個 session,斷開時則清理 session 。
在這樣的機制下,可以使用 ZooKeeper 進行監控。
如上圖所示,client 1、2、3 在連接 Zookeeper 服務器可以注冊一個臨時節點,斷開連接時 Zookeeper 就會清理臨時節點,這樣便能做到監控。
監視
ZooKeeper 對三類操作提供監視器注冊(Watches):
- getData()
- getChildren()
- exist()
監視器均為一次觸發!如果一直要監控節點變化,則需要在監視器觸發后,再次注冊監視器!
監視器事件:
- ZOO_CREATED_EVENT // 節點被創建(此前該節點不存在)
- ZOO_DELETED_EVENT // 節點被刪除
- ZOO_CHANGED_EVENT // 節點發生變化
- ZOO_CHILD_EVENT // 子節點事件
- ZOO_SESSION_EVENT // 會話丟失
- ZOO_NOTWATCHING_EVENT // 監視被移除。
- None // None事件會觸發,但是不會使當前監視器失效
Thrift && Zookeeper 的微服務架構
將 Thrift Server 發布 ZooKeeper 上(注冊臨時節點),Client 端根據 serviceName(alias) 在 ZooKeeper 上查找 Thrift Server 主機,連接所有提供該服務的 Thrift Server 主機,并自動管理所有的 TCP 連接。
ZooKeeper 結構
設置 Zookeeper 的結構如下,service 的每個節點均為臨時節點。

Thrift Service 發布
Thrift 的服務由 thrift 文件定義而成,service 創建之后,將 thrift.createServer 的 host:port 發布到 ZooKeeper/Redis 上。
- 自動獲取機器內網 ipv4 地址,eth0(linux) en0(osx)
- 自動查找一個可用的 port
- 發布 Thrift 服務 或 JS 服務
- 對于 Thrift gen-js 的 service 單獨發布
- 因為 JS 語言支持
.apply
方式的調用,所以調用 remote service 的時候,可以只傳輸alias
action
params
,結果返回時,傳輸err
result
,所以可以用一個通用的msg.thrift
用于傳輸。并且可以管理actions
的調用權限。
Thrift Client 訂閱
Client 連接ZooKeeper/Redis,根據 service alias
查找所有提供服務的 server 地址,并管理所有 server 連接。
-
action
調用權限控制。 - 輪詢調用 alias 的所有連接。
- 使用
Watcher
訂閱${service}
子節點的變化,并自動添加新 server 。 - 清理失效的連接。
- 調用服務:
-
.call(alias, action, params[, callback])
返回 js service 調用的結果。 -
.call(alias[, callback])
返回 Thrift client 供 gen-js 的調用。