Node.js 与 HBase 通信
最近在搞数据检索平台,其中一环用到了 HBase。与其他常见的数据库不同,当你安装了数据库,下载了Node.js 包,你会发现几乎所有的包都不可用,同时关于 Node.js 与 HBase 的话题又比较少,作为 HBase 初学者会很抓狂。
HBase 非集群环境搭建
下载HBase 2.4.4 bin 安装包
curl -o https://downloads.apache.org/hbase/2.4.4/hbase-2.4.4-bin.tar.gz
启动 HBase 服务
./bin/start-hbase.sh
启动 thrif2 server 服务
./bin/hbase thrift2
thrift2 服务器监听 9090 端口
HBase thrift2 客户端编译
下载,编译,安装 thrift
curl -o https://downloads.apache.org/thrift/0.14.2/thrift-0.14.2.tar.gz
tar -zxvf thrift-0.14.2.tar.gz
cd thrift-0.14.2
configure
make
make install
下载 HBase 的 thrift 文件
https://github.com/apache/hbase/blob/rel/2.4.4/hbase-thrift/src/main/resources/org/apache/hadoop/hbase/thrift2/hbase.thrift
注意: 不同 HBase 版本的 thrift 文件可能不一样,可能每个版本都需要重新编译,重新调整 Node.js 端的代码(这也是被诟病的地方)
编译 thrift, 生成 hbase_types.js
和 THBaseService.js
thrift --gen js: node hbase.thrift
Node.js 与 HBase 通信
Node.js 可以通过 REST 和 Thrift RPC 方式跟 HBase 通信,个人更倾向于 RPC 的方式,所以选用了 Thrift2 方案(Thrift2 是 Thrift 的升级版)
创建链接
const thrift = require("thrift");
const HBase = require("../gen-nodejs/THBaseService"); // 接口文件
const HBaseTypes = require("../gen-nodejs/hbase_types");
const transport = thrift.TBufferedTransport;
const protocol = thrift.TBinaryProtocol;
const connection = thrift.createConnection("localhost", 9090, {
transport: transport,
protocol: protocol,
});
connection.on("error", function (err) {
console.log("error:", err);
});
connection.on("close", function (err) {
console.log("close");
});
const client = thrift.createHttpClient(HBase, connection);
操作数据库
以创建命名空间,数据表,添加行数据,获取行数据为例
const namespace = "ns";
const tableName = "test";
const cf = "cf";
// create Namespace
client.createNamespace({ name: namespace }, function (err) {
if (err) {
console.log("create namespace error:", err);
} else {
console.log("create namespace success");
}
});
// 创建表
client.createTable(
{
tableName: { ns: namespace, qualifier: tableName },
columns: [{ name: cf }],
},
null,
function (err) {
if (err) {
console.log("create table error:", err);
} else {
console.log("create table success");
}
}
);
// 单行新增数据, 更新数据 跟插入一样
client.put(
`${namespace}:${tableName}`,
{
row: "192",
columnValues: [
{
family: "cf",
qualifier: "value",
value: "hhhh",
},
{
family: "cf",
qualifier: "value1",
value: "hhhh",
},
{
family: "cf",
qualifier: "value2",
value: "hhhh",
},
],
},
function (err) {
if (err) {
console.log("put column error", error);
} else {
console.log("put column success");
}
}
);
// 单条rowKey查询
client.get(`${namespace}:${tableName}`, { row: "192" }, function (err, data) {
if (err) {
console.log("Get error:", err);
} else {
console.log("Get result: " + JSON.stringify(data));
}
});
简单封装
以获取单条数据为例子
// 格式化行数据
function formate(rowData) {
const obj = {};
obj.rowkey = rowData.row ? rowData.row.toString() : null;
rowData.columnValues.forEach(column => {
const family = column.family.toString();
const qualName = column.qualifier.toString();
obj[family] = obj[family] || {};
obj[family][qualName] = column.value.toString()
});
return obj;
}
...
client.getAsync = promisify(client.get); // promise 化
const res = await client.getAsync(`${namespace}:${tableName}`, { row: "192" });
console.log('res', formate(res))
早期版本中,THBaseService.js
中的接口参数需要通过 hbase_types.js 中的类型进行约束,现在版本已经做了兼容处理,可以使用hbase_type进行传参数,也可以直接传 javascript 对象,例如下面两种写法是等价的。
const res = await client.createNamespaceAsync(new HBaseTypes.TNamespaceDescriptor({ name: "ns" }));
const res1 = await client.createNamespaceAsync({ name: "ns" })
待改进
- 引入连接池的概念
- 测试过程中发现,短时间内不通信链接会被断掉,可能需要引入心跳机制
- 部门成员主要对 mongo 结构熟悉,封装为 mongo 的操作形式
关注我的微信公众号"SUNTOPO WLOG",欢迎留言讨论,我会尽可能回复,感谢您的阅读。