基于 Thrift 的 Node.js 微服務

標簽: node.js zookeeper thrift 微服務


當項目越來越大的時候,必然會做到服務化,而且大型項目下,極有可能調用跨語言服務。所以我寫了這個通用的框架,用于發布/調用node服務, node-thrift-service

Thrift

Apache Thrift 是一款跨語言的服務框架,傳輸數據采用二進制格式,相對 XML 和 JSON 體積更小,對于高并發、大數據量和多語言的環境更有優勢。

client<->server
client<->server

安裝

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 的『節點』均可以存放數據,并且可以對節點的權限進行控制。

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 的每個節點均為臨時節點

zookeeper結構
zookeeper結構

Thrift Service 發布

Thrift 的服務由 thrift 文件定義而成,service 創建之后,將 thrift.createServer 的 host:port 發布到 ZooKeeper/Redis 上。

  • 自動獲取機器內網 ipv4 地址,eth0(linux) en0(osx)
  • 自動查找一個可用的 port
  • 發布 Thrift 服務 或 JS 服務
  1. 對于 Thrift gen-js 的 service 單獨發布
  2. 因為 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 。
  • 清理失效的連接。
  • 調用服務:
  1. .call(alias, action, params[, callback]) 返回 js service 調用的結果。
  2. .call(alias[, callback]) 返回 Thrift client 供 gen-js 的調用。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容