Node.js 原生接口

Node.js 原生 API 支持通过 Session 和 SessionPool 两种方式与 IoTDB 树模型进行交互,可执行数据写入、查询、非查询 SQL 以及连接管理等操作。由于 Session 非线程安全,生产环境推荐使用 SessionPool 编程。在高并发场景下,SessionPool 能够统一管理连接资源,并支持多节点负载均衡、故障转移和写入重定向。

本文将围绕 SessionPool 的使用进行说明,涵盖从环境准备、核心操作步骤到常用接口的完整内容。

1. 环境准备

1.1 前置依赖

  • Node.js >= 14.0.0
  • npm >= 6.0.0
  • IoTDB >= 2.0.11

1.2 安装方法

  • 方式一:通过 npm 直接安装(推荐)

在 Node.js 项目中执行:

npm install @iotdb/client
  • 方式二:从源码构建

如需使用仓库中的开发版本,可克隆源码并安装依赖:

git clone https://github.com/apache/iotdb-client-nodejs.git
cd iotdb-client-nodejs
git checkout develop
npm ci

Linux、macOS 或 WSL 环境执行:

npm run build

Windows PowerShell 环境执行:

npm run build:esbuild
npm run build:types
New-Item -ItemType Directory -Force -Path dist\thrift\generated
Copy-Item src\thrift\generated\*.js,src\thrift\generated\*.d.ts -Destination dist\thrift\generated -Force

构建完成后,可在业务项目中通过客户端源码目录的绝对路径进行本地安装:

npm install /absolute/path/to/iotdb-client-nodejs

如使用 TypeScript,无需额外安装类型声明,客户端已内置完整的 TypeScript 类型定义。

注意:请勿使用高版本客户端连接低版本服务。

2. 核心步骤

使用 Node.js 原生接口操作 IoTDB 树模型的三个核心步骤如下:

  1. 创建连接池实例:初始化一个 SessionPool 对象,配置连接参数和池大小。
  2. 执行数据库操作:直接通过连接池执行数据写入、查询或非查询 SQL。
  3. 关闭连接池资源:程序结束时调用 pool.close(),释放所有连接。

下面的章节用于说明开发的核心流程,并未演示所有参数和接口。如需了解完整能力,可查阅 @iotdb/client 源码及示例。

2.1 创建连接池实例

2.1.1 单节点连接

import { SessionPool } from '@iotdb/client';

const pool = new SessionPool('localhost', 6667, {
  username: 'root',
  password: 'root',
  maxPoolSize: 10,
  minPoolSize: 2,
  maxIdleTime: 60000,
  waitTimeout: 60000,
});

await pool.init();

2.1.2 多节点连接

在集群环境下,推荐使用 nodeUrls 配置多个节点。连接池会以轮询方式在多个节点间分配连接,并在连接失败时尝试其他可用节点。

import { SessionPool } from '@iotdb/client';

const pool = new SessionPool({
  nodeUrls: [
    '192.168.1.100:6667',
    '192.168.1.101:6667',
    '192.168.1.102:6667',
  ],
  username: 'root',
  password: 'root',
  maxPoolSize: 15,
  minPoolSize: 3,
});

await pool.init();

也可以使用构建器模式创建连接池配置:

import { SessionPool, PoolConfigBuilder } from '@iotdb/client';

const pool = new SessionPool(
  new PoolConfigBuilder()
    .nodeUrls([
      '192.168.1.100:6667',
      '192.168.1.101:6667',
      '192.168.1.102:6667',
    ])
    .username('root')
    .password('root')
    .maxPoolSize(15)
    .minPoolSize(3)
    .build()
);

await pool.init();

连接池参数可按业务并发量调整:minPoolSize 建议设置为平均并发负载,maxPoolSize 建议设置为峰值并发负载并预留 20% 到 30% 缓冲;maxIdleTime 用于清理长期空闲连接,waitTimeout 用于控制连接池耗尽时的最大等待时间。生产环境建议监控 getPoolSize()、getAvailableSize() 和 getInUseSize(),根据峰值负载调整连接池大小。

2.1.3 SSL/TLS 连接

如 IoTDB 服务端启用了 SSL/TLS,可在创建连接池时开启 SSL,并指定证书相关参数。

import { SessionPool } from '@iotdb/client';
import * as fs from 'fs';

const pool = new SessionPool({
  host: 'localhost',
  port: 6667,
  username: 'root',
  password: 'root',
  enableSSL: true,
  sslOptions: {
    ca: fs.readFileSync('/path/to/ca.crt'),
    cert: fs.readFileSync('/path/to/client.crt'),
    key: fs.readFileSync('/path/to/client.key'),
    rejectUnauthorized: true,
  },
});

await pool.init();

2.1.4 写入重定向

在多节点 IoTDB 集群中,客户端支持写入重定向。写入操作发送到非目标节点时,服务端可能返回重定向建议,客户端会缓存设备到节点的映射,并在后续写入同一设备时优先使用目标节点。

import { SessionPool } from '@iotdb/client';

const pool = new SessionPool({
  nodeUrls: [
    '192.168.1.100:6667',
    '192.168.1.101:6667',
    '192.168.1.102:6667',
  ],
  username: 'root',
  password: 'root',
  maxPoolSize: 10,
  enableRedirection: true,
  redirectCacheTTL: 300000,
});

await pool.init();

启用重定向后,可减少跨节点转发,提升写入吞吐并降低网络延迟。该能力适用于树模型中的设备级写入场景。

2.2 数据库操作

2.2.1 创建数据库和时间序列

await pool.executeNonQueryStatement('CREATE DATABASE root.test');

await pool.executeNonQueryStatement(
  'CREATE TIMESERIES root.test.device1.temperature WITH DATATYPE=FLOAT, ENCODING=RLE'
);

await pool.executeNonQueryStatement(
  'CREATE TIMESERIES root.test.device1.humidity WITH DATATYPE=FLOAT, ENCODING=RLE'
);

2.2.2 写入 Tablet 数据

insertTablet 支持按设备批量写入多行数据。values 为二维数组,按行组织数据:每行对应一个时间戳,与 timestamps 顺序一致;每列对应一个测点,与 measurements 顺序一致。

await pool.insertTablet({
  deviceId: 'root.test.device1',
  measurements: ['temperature', 'humidity'],
  dataTypes: [3, 3],
  timestamps: [Date.now(), Date.now() + 1000],
  values: [
    [25.5, 60.0],
    [26.0, 61.5],
  ],
});

其中 dataTypes 可使用数据类型编码,也可在项目中封装为常量使用。常用类型编码见本文第 4 章。

写入数据时建议优先使用 insertTablet 批量写入,减少网络往返次数;常见批量大小可从 100 到 1000 行开始压测,再根据数据规模、网络和服务端资源调整。

2.2.3 查询数据

查询结果通过 SessionDataSet 返回,支持分页拉取,适合处理较大的结果集。使用完成后应调用 close() 释放服务端查询资源。

const dataSet = await pool.executeQueryStatement(
  'SELECT temperature, humidity FROM root.test.device1'
);

while (await dataSet.hasNext()) {
  const row = dataSet.next();
  console.log(row.getTimestamp(), row.getFields());
}

await dataSet.close();

对于小型结果集,也可以使用 toArray() 将全部结果加载到内存:

const dataSet = await pool.executeQueryStatement('SHOW DATABASES');
const rows = await dataSet.toArray();
console.log(rows);
await dataSet.close();

2.3 关闭连接池

await pool.close();

建议在应用退出、定时任务结束或服务销毁时统一关闭连接池,避免连接泄漏。

3. 常用接口

3.1 SessionPool

3.1.1 功能描述

SessionPool 是树模型推荐使用的连接池接口,支持自动会话管理。调用查询、写入或非查询方法时,连接池会自动获取可用 Session,执行完成后回收连接。

3.1.2 构造方式

构造方式说明
new SessionPool(hosts, port, config)传统构造方式,适用于单节点或同端口多主机配置
new SessionPool(config)使用配置对象构造,适合 nodeUrls 多节点配置
new SessionPool(new PoolConfigBuilder().build())使用构建器模式构造,推荐用于参数较多的场景

3.1.3 方法列表

方法名描述
init()初始化连接池
close()关闭连接池并释放所有连接
executeQueryStatement(sql, timeoutMs?)执行查询 SQL,可设置查询超时时间
executeNonQueryStatement(sql)执行非查询 SQL,例如 DDL 或 DML
insertTablet(tablet)插入 Tablet 数据
getSession()从连接池中获取一个 Session,需要手动归还
releaseSession(session)将手动获取的 Session 释放回连接池
getPoolSize()获取当前连接池大小
getAvailableSize()获取当前可用连接数
getInUseSize()获取当前正在使用的连接数

3.1.4 配置项

配置项描述
host设置主机地址
port设置端口
nodeUrls设置多个节点地址,格式为 host:port
username设置用户名
password设置密码
database设置默认数据库
timezone设置时区
fetchSize设置查询结果批量拉取大小
maxPoolSize设置最大连接数
minPoolSize设置最小连接数
maxIdleTime设置最大空闲时间,单位为毫秒
waitTimeout设置获取连接的等待超时时间,单位为毫秒
enableSSL是否启用 SSL
sslOptions设置 SSL 参数
enableRedirection是否启用写入重定向
redirectCacheTTL设置重定向缓存过期时间,单位为毫秒

3.2 Session

3.2.1 功能描述

Session 表示一个独立会话,适用于简单脚本或单线程场景。Session 非线程安全,多线程或高并发场景请使用 SessionPool。

3.2.2 方法列表

方法名描述
open()打开会话
close()关闭会话
executeQueryStatement(sql, timeoutMs?)执行查询 SQL
executeNonQueryStatement(sql)执行非查询 SQL
insertTablet(tablet)插入 Tablet 数据
isOpen()判断会话是否已打开

4. 数据类型

插入 Tablet 数据时,需要为每个测点指定对应的数据类型。Node.js 客户端支持的常用类型如下:

类型编码类型名称JavaScript 类型说明
0BOOLEANboolean布尔值
1INT32number32 位整数
2INT64bigint64 位整数
3FLOATnumber32 位浮点数
4DOUBLEnumber64 位浮点数
5TEXTstringUTF-8 文本
8TIMESTAMPDate毫秒精度时间戳
9DATEDate日期类型
10BLOBBuffer二进制数据
11STRINGstringUTF-8 字符串

处理 INT64 类型时,建议在 JavaScript 中使用 bigint,避免超出 number 安全整数范围后出现精度损失。

5. 常见问题

  1. 连接被拒绝:如果出现 ECONNREFUSED,请检查 IoTDB 服务是否已启动、RPC 端口是否正确、网络和防火墙是否允许访问。默认端口通常为 6667。

  2. 获取连接超时:如果出现等待可用连接超时,通常说明连接池已被占满。可适当增大 waitTimeout 或 maxPoolSize,同时检查是否存在手动获取 Session 后未调用 releaseSession(session) 的情况。

  3. 查询结果占用内存过高:大结果集查询建议使用 hasNext() 和 next() 分批读取,并调小 fetchSize。仅在结果集较小时使用 toArray()。

  4. 写入性能不稳定:建议优先使用 insertTablet 批量写入,并根据数据规模、网络和服务端资源调整每批行数。多节点环境下建议配置 nodeUrls,让连接池在多个节点间分配负载。