diff --git a/documentation/webdocs/markdowndocs/connector-ch.md b/documentation/webdocs/markdowndocs/connector-ch.md index 61da53585f302d1a80a4abc5bf7a27a15bef279f..ec97816a271ccd3f7dce5ce72d052dbe36f283c7 100644 --- a/documentation/webdocs/markdowndocs/connector-ch.md +++ b/documentation/webdocs/markdowndocs/connector-ch.md @@ -917,15 +917,16 @@ TDengine 同时也提供了node.js 的连接器。用户可以通过[npm](https: 首先,通过[npm](https://www.npmjs.com/)安装node.js 连接器. ```cmd -npm install td-connector +npm install td2.0-connector ``` 我们建议用户使用npm 安装node.js连接器。如果您没有安装npm, 可以将*src/connector/nodejs/*拷贝到您的nodejs 项目目录下 我们使用[node-gyp](https://github.com/nodejs/node-gyp)和TDengine服务端进行交互。安装node.js 连接器之前,还需安装以下软件: -### Unix +### Linux - `python` (建议`v2.7` , `v3.x.x` 目前还不支持) +- `node` 必须采用v8.x版本,之后的版本存在兼容性的问题。 - `make` - c语言编译器比如[GCC](https://gcc.gnu.org) @@ -980,10 +981,10 @@ npm install td-connector #### 连接 -使用node.js连接器时,必须先require ```td-connector```,然后使用 ```taos.connect``` 函数。```taos.connect``` 函数必须提供的参数是```host```,其它参数在没有提供的情况下会使用如下的默认值。最后需要初始化```cursor``` 来和TDengine服务端通信 +使用node.js连接器时,必须先require ```td2.0-connector```,然后使用 ```taos.connect``` 函数。```taos.connect``` 函数必须提供的参数是```host```,其它参数在没有提供的情况下会使用如下的默认值。最后需要初始化```cursor``` 来和TDengine服务端通信 ```javascript -const taos = require('td-connector'); +const taos = require('td2.0-connector'); var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0}) var cursor = conn.cursor(); // Initializing a new cursor ``` diff --git a/documentation20/TAOS SQL-ch.md b/documentation20/TAOS SQL-ch.md index 024a3c4c24f6fff143bccb3e491d82b134fa4605..c8e6219fb058d7a005c11d77315ddb41993dffcd 100644 --- a/documentation20/TAOS SQL-ch.md +++ b/documentation20/TAOS SQL-ch.md @@ -947,7 +947,7 @@ SELECT function_list FROM stb_name 2. 在时间维度聚合中,返回的结果中时间序列严格单调递增。 3. 如果查询对象是超级表,则聚合函数会作用于该超级表下满足值过滤条件的所有表的数据。如果查询中没有使用group by语句,则返回的结果按照时间序列严格单调递增;如果查询中使用了group by语句分组,则返回结果中每个group内不按照时间序列严格单调递增。 -**示例:**智能电表的建表语句如下: +**示例:** 智能电表的建表语句如下: ```mysql CREATE TABLE meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int); @@ -963,7 +963,7 @@ SELECT AVG(current),MAX(current),LEASTSQUARES(current, start_val, step_val), PER FILL(PREV); ``` -##TAOS SQL 边界限制 +## TAOS SQL 边界限制 - 数据库名最大长度为33 - 表名最大长度为193,每行数据最大长度16k个字符 - 列名最大长度为65,最多允许1024列,最少需要2列,第一列必须是时间戳 diff --git a/documentation20/advanced features-ch.md b/documentation20/advanced features-ch.md index ccde21bfffa129e6e15a668ca557c46fd7625c9a..c767daddce2ff0e02d61045dec5b37548cedfd3a 100644 --- a/documentation20/advanced features-ch.md +++ b/documentation20/advanced features-ch.md @@ -296,7 +296,8 @@ $ taos 您可以继续插入一些数据观察示例程序的输出。 -## 缓存 (Cache) +## 缓存(Cache) + TDengine采用时间驱动缓存管理策略(First-In-First-Out,FIFO),又称为写驱动的缓存管理机制。这种策略有别于读驱动的数据缓存模式(Least-Recent-Use,LRU),直接将最近写入的数据保存在系统的缓存中。当缓存达到临界值的时候,将最早的数据批量写入磁盘。一般意义上来说,对于物联网数据的使用,用户最为关心最近产生的数据,即当前状态。TDengine充分利用了这一特性,将最近到达的(当前状态)数据保存在缓存中。 TDengine通过查询函数向用户提供毫秒级的数据获取能力。直接将最近到达的数据保存在缓存中,可以更加快速地响应用户针对最近一条或一批数据的查询分析,整体上提供更快的数据库查询响应能力。从这个意义上来说,可通过设置合适的配置参数将TDengine作为数据缓存来使用,而不需要再部署额外的缓存系统,可有效地简化系统架构,降低运维的成本。需要注意的是,TDengine重启以后系统的缓存将被清空,之前缓存的数据均会被批量写入磁盘,缓存的数据将不会像专门的Key-value缓存系统再将之前缓存的数据重新加载到缓存中。 @@ -308,7 +309,14 @@ TDengine将内存池按块划分进行管理,数据在内存块里按照列式 你可以通过函数last_row快速获取一张表或一张超级表的最后一条记录,这样很便于在大屏显示各设备的实时状态或采集值。例如: ```mysql -select last_row(degree) from thermometer where location='beijing'; +select last_row(voltage) from meters where location='Beijing.Chaoyang'; ``` -该SQL语句将获取所有位于北京的传感器最后记录的温度值。 +该SQL语句将获取所有位于北京朝阳区的电表最后记录的电压值。 + + +## 报警监测(Alert) + +在 TDengine 的应用场景中,报警监测是一个常见需求,从概念上说,它要求程序从最近一段时间的数据中筛选出符合一定条件的数据,并基于这些数据根据定义好的公式计算出一个结果,当这个结果符合某个条件且持续一定时间后,以某种形式通知用户。 + +为了满足用户对报警监测的需求,TDengine 以独立模块的形式提供了这一功能,有关它的安装使用方法,请参考博客 [使用 TDengine 进行报警监测](https://www.taosdata.com/blog/2020/04/14/1438.html) 。 diff --git a/documentation20/cluster-ch.md b/documentation20/cluster-ch.md new file mode 100644 index 0000000000000000000000000000000000000000..f3be262d24b5f40d9a2837dfac4aa5fa24883d71 --- /dev/null +++ b/documentation20/cluster-ch.md @@ -0,0 +1,108 @@ +# 集群管理 + +多个taosd的运行实例可以组成一个集群,以保证TDengine的高可靠运行,并提供水平扩展能力。要了解TDengine 2.0的集群管理,需要对集群的基本概念有所了解,请看TDengine 2.0整体架构一章。 + +TDengine的集群管理极其简单,除添加和删除节点需要人工干预之外,其他全部是自动完成,最大程度的降低了运维的工作量。本章对集群管理的操作做详细的描述。 + +## 创建第一个节点 +集群是由一个一个dnode组成的,是从一个dnode的创建开始的。创建第一个节点很简单,确保系统配置文件taos.cfg里的参数first与second没有进行设置后,简单的运行taosd即可。配置文件taos.cfg里参数first与second缺省都是设置为空,因此无需特殊操作。如果为了统一配置,可以将参数first配置为第一个dnode的End Point。 + +这个节点创建后,它是集群中的第一个节点。如果该节点收到一个新节点加入集群的请求,它将检查这个新节点的End Point是否在集群的EP列表中,如果在,就容许其加入。如果不在列表中,就拒绝其加入。 + +## 节点管理 +### 添加节点 +具体有以下几个步骤: + +1. 修改新节点配置文件taos.cfg, 将其中参数first与second设置为现有集群中运行节点的End Point, 然后启动taosd。second可以不用设置,目的是为集群规模较大时,first无法访问时,该节点将尝试访问second节点。 +2. 使用CLI程序taos, 登录进系统, 使用命令: + ``` + CREATE DNODE "fqdn:port"; + ``` + 将新节点的End Point添加进集群的EP列表。**"fqdn:port"需要用双引号引起来**,否则出错。一个节点对外服务的fqdn和port可以通过配置文件taos.cfg进行配置。 +3. 使用命令 + ``` + SHOW DNODES + ``` + 查看新节点是否被成功加入。` + +**示例:**假设有两个节点,其EP分别为 host1:6030, host2:6050. 可以按照以下步骤创建集群 + +1. 在host1, 启动taosd; +2. 修改host2:6050的配置文件taos.cfg, 将参数first设置为host1:6030,然后启动host2:6050这个taosd实例; +3. 执行“taos -h host1" 链接到host1, 在TDengine console里执行命令: + ``` + CREATE DNODE "host2:6050"; + ``` +4. 在TDengine console里执行`"show dnodes"`查看节点是否加入成功. +5. 按照上述第2到第5步,逐步把其他节点添加到集群中。 + +**提示:** + +- first, second这两个参数仅仅在该节点第一次加入集群时有作用,加入集群后,该节点会保存最新的mnode的End Point列表,不再依赖这两个参数。 +- 两个没有配置first, second参数的dnode启动后,会独立运行起来。这个时候,无法将其中一个节点加入到另外一个节点,形成集群。**无法将两个独立的集群合并成为新的集群**。 + +### 删除节点 +执行CLI程序taos, 使用root账号登录进TDengine系统,执行: +``` +DROP DNODE "fqdn:port"; +``` +其中fqdn是被删除的节点的FQDN,port是其对外服务器的端口号 + +### 查看节点 +执行CLI程序taos,使用root账号登录进TDengine系统,执行: +``` +SHOW DNODES; +``` +它将列出集群中所有的dnode,每个dnode的fqdn:port, 状态(ready, offline等),vnode数目,还未使用的vnode数目等信息。在添加或删除一个节点后,可以使用该命令查看。 + +### 查看虚拟节点组 + +为充分利用多核技术,并提供scalability,数据需要分片处理。因此TDengine会将一个DB的数据切分成多份,存放在多个vnode里。这些vnode可能分布在多个dnode里,这样就实现了水平扩展。一个vnode仅仅属于一个DB,但一个DB可以有多个vnode。vnode的是mnode根据当前系统资源的情况,自动进行分配的,无需任何人工干预。 + +执行CLI程序taos,使用root账号登录进TDengine系统,执行: +``` +SHOW VGROUPS; +``` +## 高可用性 +TDengine通过多副本的机制来提供系统的高可用性。副本数是与DB关联的,一个集群里可以有多个DB,根据运营的需求,每个DB可以配置不同的副本数。创建数据库时,通过参数replica 指定副本数(缺省为1)。如果副本数为1,系统的可靠性无法保证,只要数据所在的节点宕机,就将无法提供服务。集群的节点数必须大于等于副本数,否则创建表时将返回错误“more dnodes are needed"。比如下面的命令将创建副本数为3的数据库demo: +``` +CREATE DATABASE demo replica 3; +``` +一个DB里的数据会被切片分到多个vnode group,vnode group里的vnode数目就是DB的副本数,同一个vnode group里各vnode的数据是完全一致的。为保证高可用性,vnode group里的vnode一定要分布在不同的dnode里(实际部署时,需要在不同的物理机上),只要一个vgroup里超过半数的vnode处于工作状态,这个vgroup就能正常的对外服务。 + +一个dnode里可能有多个DB的数据,因此一个dnode离线时,可能会影响到多个DB。如果一个vnode group里的一半或一半以上的vnode不工作,那么该vnode group就无法对外服务,无法插入或读取数据,这样会影响到它所属的DB的一部分表的d读写操作。 + +因为vnode的引入,无法简单的给出结论:“集群中过半dnode工作,集群就应该工作”。但是对于简单的情形,很好下结论。比如副本数为3,只有三个dnode,那如果仅有一个节点不工作,整个集群还是可以正常工作的,但如果有两个节点不工作,那整个集群就无法正常工作了。 + +## Mnode的高可用 +TDengine集群是由mnode (taosd的一个模块,逻辑节点) 负责管理的,为保证mnode的高可用,可以配置多个mnode副本,副本数由系统配置参数numOfMnodes决定,有效范围为1-3。为保证元数据的强一致性,mnode副本之间是通过同步的方式进行数据复制的。 + +一个集群有多个dnode, 但一个dnode至多运行一个mnode实例。多个dnode情况下,哪个dnode可以作为mnode呢?这是完全由系统根据整个系统资源情况,自动指定的。用户可通过CLI程序taos,在TDengine的console里,执行如下命令: +``` +SHOW MNODES; +``` +来查看mnode列表,该列表将列出mnode所处的dnode的End Point和角色(master, slave, unsynced 或offline)。 +当集群中第一个节点启动时,该节点一定会运行一个mnode实例,否则该dnode无法正常工作,因为一个系统是必须有至少一个mnode的。如果numOfMnodes配置为2,启动第二个dnode时,该dnode也将运行一个mnode实例。 + +为保证mnode服务的高可用性,numOfMnodes必须设置为2或更大。因为mnode保存的元数据必须是强一致的,如果numOfMnodes大于2,复制参数quorum自动设为2,也就是说,至少要保证有两个副本写入数据成功,才通知客户端应用写入成功。 + +## 负载均衡 + +有三种情况,将触发负载均衡,而且都无需人工干预。 + +- 当一个新节点添加进集群时,系统将自动触发负载均衡,一些节点上的数据将被自动转移到新节点上,无需任何人工干预。 +- 当一个节点从集群中移除时,系统将自动把该节点上的数据转移到其他节点,无需任何人工干预。 +- 如果一个节点过热(数据量过大),系统将自动进行负载均衡,将该节点的一些vnode自动挪到其他节点。 + +当上述三种情况发生时,系统将启动一各个节点的负载计算,从而决定如何挪动。 + +## 节点离线处理 +如果一个节点离线,TDengine集群将自动检测到。有如下两种情况: +- 改节点离线超过一定时间(taos.cfg里配置参数offlineThreshold控制时长),系统将自动把该节点删除,产生系统报警信息,触发负载均衡流程。如果该被删除的节点重现上线时,它将无法加入集群,需要系统管理员重新将其添加进集群才会开始工作。 +- 离线后,在offlineThreshold的时长内重新上线,系统将自动启动数据恢复流程,等数据完全恢复后,该节点将开始正常工作。 + +## Arbitrator的使用 + +如果副本数为偶数,当一个vnode group里一半vnode不工作时,是无法从中选出master的。同理,一半mnode不工作时,是无法选出mnode的master的,因为存在“split brain”问题。为解决这个问题,TDengine引入了arbitrator的概念。Arbitrator模拟一个vnode或mnode在工作,但只简单的负责网络连接,不处理任何数据插入或访问。只要包含arbitrator在内,超过半数的vnode或mnode工作,那么该vnode group或mnode组就可以正常的提供数据插入或查询服务。比如对于副本数为2的情形,如果一个节点A离线,但另外一个节点B正常,而且能连接到arbitrator, 那么节点B就能正常工作。 + +TDengine安装包里带有一个执行程序tarbitrator, 找任何一台Linux服务器运行它即可。该程序对系统资源几乎没有要求,只需要保证有网络连接即可。该应用的命令行参数`-p`可以指定其对外服务的端口号,缺省是6030。配置每个taosd实例时,可以在配置文件taos.cfg里将参数arbitrator设置为arbitrator的End Point。如果该参数配置了,当副本数为偶数数,系统将自动连接配置的arbitrator。 diff --git a/documentation20/cluster.md b/documentation20/cluster.md index dbef70343efc29406fd90eb717f0cabf9a3568d7..f3be262d24b5f40d9a2837dfac4aa5fa24883d71 100644 --- a/documentation20/cluster.md +++ b/documentation20/cluster.md @@ -1,17 +1,18 @@ -#集群管理 +# 集群管理 多个taosd的运行实例可以组成一个集群,以保证TDengine的高可靠运行,并提供水平扩展能力。要了解TDengine 2.0的集群管理,需要对集群的基本概念有所了解,请看TDengine 2.0整体架构一章。 TDengine的集群管理极其简单,除添加和删除节点需要人工干预之外,其他全部是自动完成,最大程度的降低了运维的工作量。本章对集群管理的操作做详细的描述。 -##创建第一个节点 +## 创建第一个节点 集群是由一个一个dnode组成的,是从一个dnode的创建开始的。创建第一个节点很简单,确保系统配置文件taos.cfg里的参数first与second没有进行设置后,简单的运行taosd即可。配置文件taos.cfg里参数first与second缺省都是设置为空,因此无需特殊操作。如果为了统一配置,可以将参数first配置为第一个dnode的End Point。 这个节点创建后,它是集群中的第一个节点。如果该节点收到一个新节点加入集群的请求,它将检查这个新节点的End Point是否在集群的EP列表中,如果在,就容许其加入。如果不在列表中,就拒绝其加入。 -##节点管理 -###添加节点 +## 节点管理 +### 添加节点 具体有以下几个步骤: + 1. 修改新节点配置文件taos.cfg, 将其中参数first与second设置为现有集群中运行节点的End Point, 然后启动taosd。second可以不用设置,目的是为集群规模较大时,first无法访问时,该节点将尝试访问second节点。 2. 使用CLI程序taos, 登录进系统, 使用命令: ``` @@ -40,21 +41,21 @@ TDengine的集群管理极其简单,除添加和删除节点需要人工干预 - first, second这两个参数仅仅在该节点第一次加入集群时有作用,加入集群后,该节点会保存最新的mnode的End Point列表,不再依赖这两个参数。 - 两个没有配置first, second参数的dnode启动后,会独立运行起来。这个时候,无法将其中一个节点加入到另外一个节点,形成集群。**无法将两个独立的集群合并成为新的集群**。 -###删除节点 +### 删除节点 执行CLI程序taos, 使用root账号登录进TDengine系统,执行: ``` DROP DNODE "fqdn:port"; ``` 其中fqdn是被删除的节点的FQDN,port是其对外服务器的端口号 -###查看节点 +### 查看节点 执行CLI程序taos,使用root账号登录进TDengine系统,执行: ``` SHOW DNODES; ``` 它将列出集群中所有的dnode,每个dnode的fqdn:port, 状态(ready, offline等),vnode数目,还未使用的vnode数目等信息。在添加或删除一个节点后,可以使用该命令查看。 -###查看虚拟节点组 +### 查看虚拟节点组 为充分利用多核技术,并提供scalability,数据需要分片处理。因此TDengine会将一个DB的数据切分成多份,存放在多个vnode里。这些vnode可能分布在多个dnode里,这样就实现了水平扩展。一个vnode仅仅属于一个DB,但一个DB可以有多个vnode。vnode的是mnode根据当前系统资源的情况,自动进行分配的,无需任何人工干预。 @@ -62,7 +63,7 @@ SHOW DNODES; ``` SHOW VGROUPS; ``` -##高可用性 +## 高可用性 TDengine通过多副本的机制来提供系统的高可用性。副本数是与DB关联的,一个集群里可以有多个DB,根据运营的需求,每个DB可以配置不同的副本数。创建数据库时,通过参数replica 指定副本数(缺省为1)。如果副本数为1,系统的可靠性无法保证,只要数据所在的节点宕机,就将无法提供服务。集群的节点数必须大于等于副本数,否则创建表时将返回错误“more dnodes are needed"。比如下面的命令将创建副本数为3的数据库demo: ``` CREATE DATABASE demo replica 3; @@ -73,7 +74,7 @@ CREATE DATABASE demo replica 3; 因为vnode的引入,无法简单的给出结论:“集群中过半dnode工作,集群就应该工作”。但是对于简单的情形,很好下结论。比如副本数为3,只有三个dnode,那如果仅有一个节点不工作,整个集群还是可以正常工作的,但如果有两个节点不工作,那整个集群就无法正常工作了。 -##Mnode的高可用 +## Mnode的高可用 TDengine集群是由mnode (taosd的一个模块,逻辑节点) 负责管理的,为保证mnode的高可用,可以配置多个mnode副本,副本数由系统配置参数numOfMnodes决定,有效范围为1-3。为保证元数据的强一致性,mnode副本之间是通过同步的方式进行数据复制的。 一个集群有多个dnode, 但一个dnode至多运行一个mnode实例。多个dnode情况下,哪个dnode可以作为mnode呢?这是完全由系统根据整个系统资源情况,自动指定的。用户可通过CLI程序taos,在TDengine的console里,执行如下命令: @@ -85,7 +86,7 @@ SHOW MNODES; 为保证mnode服务的高可用性,numOfMnodes必须设置为2或更大。因为mnode保存的元数据必须是强一致的,如果numOfMnodes大于2,复制参数quorum自动设为2,也就是说,至少要保证有两个副本写入数据成功,才通知客户端应用写入成功。 -##负载均衡 +## 负载均衡 有三种情况,将触发负载均衡,而且都无需人工干预。 @@ -95,12 +96,12 @@ SHOW MNODES; 当上述三种情况发生时,系统将启动一各个节点的负载计算,从而决定如何挪动。 -##节点离线处理 +## 节点离线处理 如果一个节点离线,TDengine集群将自动检测到。有如下两种情况: - 改节点离线超过一定时间(taos.cfg里配置参数offlineThreshold控制时长),系统将自动把该节点删除,产生系统报警信息,触发负载均衡流程。如果该被删除的节点重现上线时,它将无法加入集群,需要系统管理员重新将其添加进集群才会开始工作。 - 离线后,在offlineThreshold的时长内重新上线,系统将自动启动数据恢复流程,等数据完全恢复后,该节点将开始正常工作。 -##Arbitrator的使用 +## Arbitrator的使用 如果副本数为偶数,当一个vnode group里一半vnode不工作时,是无法从中选出master的。同理,一半mnode不工作时,是无法选出mnode的master的,因为存在“split brain”问题。为解决这个问题,TDengine引入了arbitrator的概念。Arbitrator模拟一个vnode或mnode在工作,但只简单的负责网络连接,不处理任何数据插入或访问。只要包含arbitrator在内,超过半数的vnode或mnode工作,那么该vnode group或mnode组就可以正常的提供数据插入或查询服务。比如对于副本数为2的情形,如果一个节点A离线,但另外一个节点B正常,而且能连接到arbitrator, 那么节点B就能正常工作。 diff --git a/documentation20/insert-ch.md b/documentation20/insert-ch.md index fcc061674246da3c5b5389e3a1fdcf67b36ccccb..2fd869cfe64ac357a8c60f2d3f765722b53e4e53 100644 --- a/documentation20/insert-ch.md +++ b/documentation20/insert-ch.md @@ -22,10 +22,10 @@ INSERT INTO d1001 VALUES (1538548685000, 10.3, 219, 0.31) (1538548695000, 12.6, 详细的SQL INSERT语法规则请见TAOS SQL -##Prometheus直接写入 +## Prometheus直接写入 [Prometheus](https://www.prometheus.io/)作为Cloud Native Computing Fundation毕业的项目,在性能监控以及K8S性能监控领域有着非常广泛的应用。TDengine提供一个小工具[Bailongma](https://github.com/taosdata/Bailongma),只需在Prometheus做简单配置,无需任何代码,就可将Prometheus采集的数据直接写入TDengine,并按规则在TDengine自动创建库和相关表项。博文[用Docker容器快速搭建一个Devops监控Demo](https://www.taosdata.com/blog/2020/02/03/1189.html)即是采用bailongma将Prometheus和Telegraf的数据写入TDengine中的示例,可以参考。 -###从源代码编译blm_prometheus +### 从源代码编译blm_prometheus 用户需要从github下载[Bailongma](https://github.com/taosdata/Bailongma)的源码,使用Golang语言编译器编译生成可执行文件。在开始编译前,需要准备好以下条件: - Linux操作系统的服务器 - 安装好Golang, 1.10版本以上 @@ -39,7 +39,7 @@ go build 一切正常的情况下,就会在对应的目录下生成一个blm_prometheus的可执行程序。 -###安装Prometheus +### 安装Prometheus 通过Prometheus的官网下载安装。[下载地址](https://prometheus.io/download/) ### 配置Prometheus @@ -70,7 +70,7 @@ blm_prometheus会将收到的prometheus的数据拼装成TDengine的写入请求 blm_prometheus对prometheus提供服务的端口号。 ``` -###启动示例 +### 启动示例 通过以下命令启动一个blm_prometheus的API服务 ``` @@ -82,7 +82,7 @@ remote_write: - url: "http://10.1.2.3:8088/receive" ``` -###查询prometheus写入数据 +### 查询prometheus写入数据 prometheus产生的数据格式如下: ``` Timestamp: 1576466279341, @@ -104,10 +104,10 @@ use prometheus; select * from apiserver_request_latencies_bucket; ``` -##Telegraf直接写入 +## Telegraf直接写入 [Telegraf](https://www.influxdata.com/time-series-platform/telegraf/)是一流行的IT运维数据采集开源工具,TDengine提供一个小工具[Bailongma](https://github.com/taosdata/Bailongma),只需在Telegraf做简单配置,无需任何代码,就可将Telegraf采集的数据直接写入TDengine,并按规则在TDengine自动创建库和相关表项。博文[用Docker容器快速搭建一个Devops监控Demo](https://www.taosdata.com/blog/2020/02/03/1189.html)即是采用bailongma将Prometheus和Telegraf的数据写入TDengine中的示例,可以参考。 -###从源代码编译blm_telegraf +### 从源代码编译blm_telegraf 用户需要从github下载[Bailongma](https://github.com/taosdata/Bailongma)的源码,使用Golang语言编译器编译生成可执行文件。在开始编译前,需要准备好以下条件: - Linux操作系统的服务器 @@ -123,7 +123,7 @@ go build 一切正常的情况下,就会在对应的目录下生成一个blm_telegraf的可执行程序。 -###安装Telegraf +### 安装Telegraf 目前TDengine支持Telegraf 1.7.4以上的版本。用户可以根据当前的操作系统,到Telegraf官网下载安装包,并执行安装。下载地址如下:https://portal.influxdata.com/downloads ### 配置Telegraf @@ -142,7 +142,7 @@ go build 关于如何使用Telegraf采集数据以及更多有关使用Telegraf的信息,请参考Telegraf官方的[文档](https://docs.influxdata.com/telegraf/v1.11/)。 -###启动blm_telegraf程序 +### 启动blm_telegraf程序 blm_telegraf程序有以下选项,在启动blm_telegraf程序时可以通过设定这些选项来设定blm_telegraf的配置。 ```sh @@ -165,7 +165,7 @@ blm_telegraf会将收到的telegraf的数据拼装成TDengine的写入请求, blm_telegraf对telegraf提供服务的端口号。 ``` -###启动示例 +### 启动示例 通过以下命令启动一个blm_telegraf的API服务 ``` ./blm_telegraf -host 127.0.0.1 -port 8089 @@ -177,7 +177,7 @@ blm_telegraf对telegraf提供服务的端口号。 url = "http://10.1.2.3:8089/telegraf" ``` -###查询telegraf写入数据 +### 查询telegraf写入数据 telegraf产生的数据格式如下: ``` { @@ -210,7 +210,7 @@ use telegraf; select * from cpu; ``` -## EMQ X MQTT Broker直接写入 +## EMQ X Broker直接写入 MQTT是一流行的物联网数据传输协议,[EMQ](https://github.com/emqx/emqx)是一开源的MQTT Broker软件,无需任何代码,只需要在EMQ里做简单配置,即可将MQTT的数据直接写入TDengine。 diff --git a/packaging/tools/install.sh b/packaging/tools/install.sh index 47a8febe35c299b2934df75334890cc340eadc34..aee4bbadf670f0b94cf5f8eb588b469299e9e092 100644 --- a/packaging/tools/install.sh +++ b/packaging/tools/install.sh @@ -31,7 +31,7 @@ install_main_dir="/usr/local/taos" bin_dir="/usr/local/taos/bin" # v1.5 jar dir -v15_java_app_dir="/usr/local/lib/taos" +#v15_java_app_dir="/usr/local/lib/taos" service_config_dir="/etc/systemd/system" nginx_port=6060 @@ -189,19 +189,19 @@ function install_bin() { function install_lib() { # Remove links ${csudo} rm -f ${lib_link_dir}/libtaos.* || : - ${csudo} rm -rf ${v15_java_app_dir} || : + #${csudo} rm -rf ${v15_java_app_dir} || : ${csudo} cp -rf ${script_dir}/driver/* ${install_main_dir}/driver && ${csudo} chmod 777 ${install_main_dir}/driver/* ${csudo} ln -s ${install_main_dir}/driver/libtaos.* ${lib_link_dir}/libtaos.so.1 ${csudo} ln -s ${lib_link_dir}/libtaos.so.1 ${lib_link_dir}/libtaos.so - if [ "$verMode" == "cluster" ]; then - # Compatible with version 1.5 - ${csudo} mkdir -p ${v15_java_app_dir} - ${csudo} ln -s ${install_main_dir}/connector/taos-jdbcdriver-1.0.2-dist.jar ${v15_java_app_dir}/JDBCDriver-1.0.2-dist.jar - ${csudo} chmod 777 ${v15_java_app_dir} || : - fi + #if [ "$verMode" == "cluster" ]; then + # # Compatible with version 1.5 + # ${csudo} mkdir -p ${v15_java_app_dir} + # ${csudo} ln -s ${install_main_dir}/connector/taos-jdbcdriver-1.0.2-dist.jar ${v15_java_app_dir}/JDBCDriver-1.0.2-dist.jar + # ${csudo} chmod 777 ${v15_java_app_dir} || : + #fi } function install_header() { @@ -223,42 +223,39 @@ function install_config() { ${csudo} cp -f ${script_dir}/cfg/taos.cfg ${install_main_dir}/cfg/taos.cfg.org ${csudo} ln -s ${cfg_install_dir}/taos.cfg ${install_main_dir}/cfg - if [ "$verMode" == "cluster" ]; then - [ ! -z $1 ] && return 0 || : # only install client + [ ! -z $1 ] && return 0 || : # only install client - if ((${update_flag}==1)); then - return 0 - fi - - if [ "$interactiveFqdn" == "no" ]; then - return 0 - fi - - #FQDN_FORMAT="(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" - #FQDN_FORMAT="(:[1-6][0-9][0-9][0-9][0-9]$)" - #PORT_FORMAT="(/[1-6][0-9][0-9][0-9][0-9]?/)" - #FQDN_PATTERN=":[0-9]{1,5}$" + if ((${update_flag}==1)); then + return 0 + fi + + if [ "$interactiveFqdn" == "no" ]; then + return 0 + fi - # first full-qualified domain name (FQDN) for TDengine cluster system - echo - echo -e -n "${GREEN}Enter FQDN:port (like h1.taosdata.com:6030) of an existing TDengine cluster node to join OR leave it blank to build one${NC} :" - read firstEp - while true; do - if [ ! -z "$firstEp" ]; then - # check the format of the firstEp - #if [[ $firstEp == $FQDN_PATTERN ]]; then - # Write the first FQDN to configuration file - ${csudo} sed -i -r "s/#*\s*(firstEp\s*).*/\1$firstEp/" ${cfg_install_dir}/taos.cfg - break - #else - # read -p "Please enter the correct FQDN:port: " firstEp - #fi - else + #FQDN_FORMAT="(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" + #FQDN_FORMAT="(:[1-6][0-9][0-9][0-9][0-9]$)" + #PORT_FORMAT="(/[1-6][0-9][0-9][0-9][0-9]?/)" + #FQDN_PATTERN=":[0-9]{1,5}$" + + # first full-qualified domain name (FQDN) for TDengine cluster system + echo + echo -e -n "${GREEN}Enter FQDN:port (like h1.taosdata.com:6030) of an existing TDengine cluster node to join OR leave it blank to build one${NC} :" + read firstEp + while true; do + if [ ! -z "$firstEp" ]; then + # check the format of the firstEp + #if [[ $firstEp == $FQDN_PATTERN ]]; then + # Write the first FQDN to configuration file + ${csudo} sed -i -r "s/#*\s*(firstEp\s*).*/\1$firstEp/" ${cfg_install_dir}/taos.cfg break - fi - done - - fi + #else + # read -p "Please enter the correct FQDN:port: " firstEp + #fi + else + break + fi + done } @@ -563,12 +560,11 @@ function update_TDengine() { if [ -z $1 ]; then install_bin install_service - install_config + install_config + openresty_work=false if [ "$verMode" == "cluster" ]; then # Check if openresty is installed - openresty_work=false - # Check if nginx is installed successfully if type curl &> /dev/null; then if curl -sSf http://127.0.0.1:${nginx_port} &> /dev/null; then @@ -580,8 +576,8 @@ function update_TDengine() { fi fi - echo - echo -e "\033[44;32;1mTDengine is updated successfully!${NC}" + #echo + #echo -e "\033[44;32;1mTDengine is updated successfully!${NC}" echo echo -e "${GREEN_DARK}To configure TDengine ${NC}: edit /etc/taos/taos.cfg" if ((${service_mod}==0)); then @@ -592,15 +588,12 @@ function update_TDengine() { echo -e "${GREEN_DARK}To start TDengine ${NC}: ./taosd${NC}" fi - if [ "$verMode" == "cluster" ]; then - if [ ${openresty_work} = 'true' ]; then - echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell OR from ${GREEN_UNDERLINE}http://127.0.0.1:${nginx_port}${NC}" - else - echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}" - fi + if [ ${openresty_work} = 'true' ]; then + echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell OR from ${GREEN_UNDERLINE}http://127.0.0.1:${nginx_port}${NC}" else - echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}" + echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}" fi + echo echo -e "\033[44;32;1mTDengine is updated successfully!${NC}" else @@ -643,8 +636,8 @@ function install_TDengine() { install_bin install_service - if [ "$verMode" == "cluster" ]; then - openresty_work=false + openresty_work=false + if [ "$verMode" == "cluster" ]; then # Check if nginx is installed successfully if type curl &> /dev/null; then if curl -sSf http://127.0.0.1:${nginx_port} &> /dev/null; then @@ -669,21 +662,19 @@ function install_TDengine() { echo -e "${GREEN_DARK}To start TDengine ${NC}: ${csudo} service taosd start${NC}" else echo -e "${GREEN_DARK}To start TDengine ${NC}: taosd${NC}" + fi + + if [ ${openresty_work} = 'true' ]; then + echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell OR from ${GREEN_UNDERLINE}http://127.0.0.1:${nginx_port}${NC}" + else + echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}" fi - if [ "$verMode" == "cluster" ]; then - if [ ${openresty_work} = 'true' ]; then - echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell OR from ${GREEN_UNDERLINE}http://127.0.0.1:${nginx_port}${NC}" - else - echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}" - fi - else - echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}" + if [ ! -z "$firstEp" ]; then + echo + echo -e "${GREEN_DARK}Please run${NC}: taos -h $firstEp ${GREEN_DARK} to login into cluster, then execute ${NC}: create dnode 'newDnodeFQDN:port'; ${GREEN_DARK}in TAOS shell to add this new node into the clsuter${NC}" + echo fi - - echo - echo -e "${GREEN_DARK}Please run${NC}: taos -h $firstEp ${GREEN_DARK} to login into cluster, then execute ${NC}: create dnode 'newDnodeFQDN:port'; ${GREEN_DARK}in TAOS shell to add this new node into the clsuter${NC}" - echo echo -e "\033[44;32;1mTDengine is installed successfully!${NC}" echo else # Only install client diff --git a/packaging/tools/post.sh b/packaging/tools/post.sh index eea36f4484707e8ae1a0a18b5296f7aa1d899ee0..ef66f9fa4fecb8d0e2289283af794127b33cfbb0 100755 --- a/packaging/tools/post.sh +++ b/packaging/tools/post.sh @@ -106,6 +106,29 @@ function install_config() { ${csudo} mv ${cfg_dir}/taos.cfg ${cfg_dir}/taos.cfg.org ${csudo} ln -s ${cfg_install_dir}/taos.cfg ${cfg_dir} + #FQDN_FORMAT="(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" + #FQDN_FORMAT="(:[1-6][0-9][0-9][0-9][0-9]$)" + #PORT_FORMAT="(/[1-6][0-9][0-9][0-9][0-9]?/)" + #FQDN_PATTERN=":[0-9]{1,5}$" + + # first full-qualified domain name (FQDN) for TDengine cluster system + echo + echo -e -n "${GREEN}Enter FQDN:port (like h1.taosdata.com:6030) of an existing TDengine cluster node to join OR leave it blank to build one${NC} :" + read firstEp + while true; do + if [ ! -z "$firstEp" ]; then + # check the format of the firstEp + #if [[ $firstEp == $FQDN_PATTERN ]]; then + # Write the first FQDN to configuration file + ${csudo} sed -i -r "s/#*\s*(firstEp\s*).*/\1$firstEp/" ${cfg_install_dir}/taos.cfg + break + #else + # read -p "Please enter the correct FQDN:port: " firstEp + #fi + else + break + fi + done } function clean_service_on_sysvinit() { @@ -227,8 +250,8 @@ function install_TDengine() { install_config # Ask if to start the service - echo - echo -e "\033[44;32;1mTDengine is installed successfully!${NC}" + #echo + #echo -e "\033[44;32;1mTDengine is installed successfully!${NC}" echo echo -e "${GREEN_DARK}To configure TDengine ${NC}: edit /etc/taos/taos.cfg" if ((${service_mod}==0)); then @@ -241,7 +264,12 @@ function install_TDengine() { fi echo -e "${GREEN_DARK}To access TDengine ${NC}: use ${GREEN_UNDERLINE}taos${NC} in shell${NC}" - + + if [ ! -z "$firstEp" ]; then + echo + echo -e "${GREEN_DARK}Please run${NC}: taos -h $firstEp ${GREEN_DARK} to login into cluster, then execute ${NC}: create dnode 'newDnodeFQDN:port'; ${GREEN_DARK}in TAOS shell to add this new node into the clsuter${NC}" + echo + fi echo echo -e "\033[44;32;1mTDengine is installed successfully!${NC}" } diff --git a/src/connector/nodejs/nodetaos/cinterface.js b/src/connector/nodejs/nodetaos/cinterface.js index d076beb8c048e885365e82f976de9f135a0d1c27..656741ea16fd607212711fb694e3e20e6007b2ac 100644 --- a/src/connector/nodejs/nodetaos/cinterface.js +++ b/src/connector/nodejs/nodetaos/cinterface.js @@ -10,6 +10,7 @@ const Struct = require('ref-struct'); const FieldTypes = require('./constants'); const errors = require ('./error'); const TaosObjects = require('./taosobjects'); +const { NULL_POINTER } = require('ref'); module.exports = CTaosInterface; @@ -177,9 +178,10 @@ var char_arr = ArrayType(ref.types.char); var TaosField = Struct({ 'name': char_arr, }); -TaosField.fields.name.type.size = 64; -TaosField.defineProperty('bytes', ref.types.short); +TaosField.fields.name.type.size = 65; TaosField.defineProperty('type', ref.types.char); +TaosField.defineProperty('bytes', ref.types.short); + /** * @@ -202,10 +204,10 @@ function CTaosInterface (config = null, pass = false) { 'taos_connect': [ ref.types.void_ptr, [ ref.types.char_ptr, ref.types.char_ptr, ref.types.char_ptr, ref.types.char_ptr, ref.types.int ] ], //void taos_close(TAOS *taos) 'taos_close': [ ref.types.void, [ ref.types.void_ptr ] ], - //TAOS_RES *taos_use_result(TAOS *taos); - 'taos_use_result': [ ref.types.void_ptr, [ ref.types.void_ptr ] ], + //int *taos_fetch_lengths(TAOS_RES *taos); + 'taos_fetch_lengths': [ ref.types.void_ptr, [ ref.types.void_ptr ] ], //int taos_query(TAOS *taos, char *sqlstr) - 'taos_query': [ ref.types.int, [ ref.types.void_ptr, ref.types.char_ptr ] ], + 'taos_query': [ ref.types.void_ptr, [ ref.types.void_ptr, ref.types.char_ptr ] ], //int taos_affected_rows(TAOS *taos) 'taos_affected_rows': [ ref.types.int, [ ref.types.void_ptr] ], //int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows) @@ -329,22 +331,22 @@ CTaosInterface.prototype.query = function query(connection, sql) { CTaosInterface.prototype.affectedRows = function affectedRows(connection) { return this.libtaos.taos_affected_rows(connection); } -CTaosInterface.prototype.useResult = function useResult(connection) { - let result = this.libtaos.taos_use_result(connection); +CTaosInterface.prototype.useResult = function useResult(result) { + let fields = []; let pfields = this.fetchFields(result); if (ref.isNull(pfields) == false) { - pfields = ref.reinterpret(pfields, this.fieldsCount(connection) * 68, 0); + pfields = ref.reinterpret(pfields, this.fieldsCount(result) * 68, 0); for (let i = 0; i < pfields.length; i += 68) { //0 - 63 = name //64 - 65 = bytes, 66 - 67 = type fields.push( { - name: ref.readCString(ref.reinterpret(pfields,64,i)), - bytes: pfields[i + 64], - type: pfields[i + 66] + name: ref.readCString(ref.reinterpret(pfields,65,i)), + type: pfields[i + 65], + bytes: pfields[i + 66] }) } } - return {result:result, fields:fields} + return fields; } CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) { let pblock = ref.ref(ref.ref(ref.NULL)); // equal to our raw data @@ -352,19 +354,33 @@ CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) { if (num_of_rows == 0) { return {block:null, num_of_rows:0}; } - let isMicro = (this.libtaos.taos_result_precision(result) == FieldTypes.C_TIMESTAMP_MICRO) + var fieldL = this.libtaos.taos_fetch_lengths(result); + + let isMicro = (this.libtaos.taos_result_precision(result) == FieldTypes.C_TIMESTAMP_MICRO); + + var fieldlens = []; + + if (ref.isNull(fieldL) == false) { + + for (let i = 0; i < fields.length; i ++) { + let plen = ref.reinterpret(fieldL, 4, i*4); + let len = plen.readInt32LE(0); + fieldlens.push(len); + } + } + let blocks = new Array(fields.length); blocks.fill(null); num_of_rows = Math.abs(num_of_rows); let offset = 0; pblock = pblock.deref(); for (let i = 0; i < fields.length; i++) { - + pdata = ref.reinterpret(pblock,8,i*8); + pdata = ref.ref(pdata.readPointer()); if (!convertFunctions[fields[i]['type']] ) { throw new errors.DatabaseError("Invalid data type returned from database"); } - blocks[i] = convertFunctions[fields[i]['type']](pblock, num_of_rows, fields[i]['bytes'], offset, isMicro); - offset += fields[i]['bytes'] * num_of_rows; + blocks[i] = convertFunctions[fields[i]['type']](pdata, 1, fieldlens[i], offset, isMicro); } return {blocks: blocks, num_of_rows:Math.abs(num_of_rows)} } @@ -381,17 +397,17 @@ CTaosInterface.prototype.numFields = function numFields(result) { return this.libtaos.taos_num_fields(result); } // Fetch fields count by connection, the latest query -CTaosInterface.prototype.fieldsCount = function fieldsCount(connection) { - return this.libtaos.taos_field_count(connection); +CTaosInterface.prototype.fieldsCount = function fieldsCount(result) { + return this.libtaos.taos_field_count(result); } CTaosInterface.prototype.fetchFields = function fetchFields(result) { return this.libtaos.taos_fetch_fields(result); } -CTaosInterface.prototype.errno = function errno(connection) { - return this.libtaos.taos_errno(connection); +CTaosInterface.prototype.errno = function errno(result) { + return this.libtaos.taos_errno(result); } -CTaosInterface.prototype.errStr = function errStr(connection) { - return ref.readCString(this.libtaos.taos_errstr(connection)); +CTaosInterface.prototype.errStr = function errStr(result) { + return ref.readCString(this.libtaos.taos_errstr(result)); } // Async CTaosInterface.prototype.query_a = function query_a(connection, sql, callback, param = ref.ref(ref.NULL)) { @@ -412,18 +428,32 @@ CTaosInterface.prototype.fetch_rows_a = function fetch_rows_a(result, callback, // Data preparation to pass to cursor. Could be bottleneck in query execution callback times. let row = cti.libtaos.taos_fetch_row(result2); let fields = cti.fetchFields_a(result2); + let isMicro = (cti.libtaos.taos_result_precision(result2) == FieldTypes.C_TIMESTAMP_MICRO); let blocks = new Array(fields.length); blocks.fill(null); numOfRows2 = Math.abs(numOfRows2); let offset = 0; + var fieldL = cti.libtaos.taos_fetch_lengths(result); + var fieldlens = []; + if (ref.isNull(fieldL) == false) { + + for (let i = 0; i < fields.length; i ++) { + let plen = ref.reinterpret(fieldL, 8, i*8); + let len = ref.get(plen,0,ref.types.int32); + fieldlens.push(len); + } + } if (numOfRows2 > 0){ for (let i = 0; i < fields.length; i++) { if (!convertFunctions[fields[i]['type']] ) { throw new errors.DatabaseError("Invalid data type returned from database"); } - blocks[i] = convertFunctions[fields[i]['type']](row, numOfRows2, fields[i]['bytes'], offset, isMicro); - offset += fields[i]['bytes'] * numOfRows2; + let prow = ref.reinterpret(row,8,i*8); + prow = prow.readPointer(); + prow = ref.ref(prow); + blocks[i] = convertFunctions[fields[i]['type']](prow, 1, fieldlens[i], offset, isMicro); + //offset += fields[i]['bytes'] * numOfRows2; } } callback(param2, result2, numOfRows2, blocks); @@ -440,11 +470,11 @@ CTaosInterface.prototype.fetchFields_a = function fetchFields_a (result) { if (ref.isNull(pfields) == false) { pfields = ref.reinterpret(pfields, 68 * pfieldscount , 0); for (let i = 0; i < pfields.length; i += 68) { - //0 - 63 = name //64 - 65 = bytes, 66 - 67 = type + //0 - 64 = name //65 = type, 66 - 67 = bytes fields.push( { - name: ref.readCString(ref.reinterpret(pfields,64,i)), - bytes: pfields[i + 64], - type: pfields[i + 66] + name: ref.readCString(ref.reinterpret(pfields,65,i)), + type: pfields[i + 65], + bytes: pfields[i + 66] }) } } diff --git a/src/connector/nodejs/nodetaos/connection.js b/src/connector/nodejs/nodetaos/connection.js index bb7651544ab937f1c8ecb3ffdaa8a6b6e74a9e60..08186f87053ad0ed0982ec8941f0cf38c4ad0467 100644 --- a/src/connector/nodejs/nodetaos/connection.js +++ b/src/connector/nodejs/nodetaos/connection.js @@ -74,9 +74,11 @@ TDengineConnection.prototype.rollback = function rollback() { * Clear the results from connector * @private */ -TDengineConnection.prototype._clearResultSet = function _clearResultSet() { +/* + TDengineConnection.prototype._clearResultSet = function _clearResultSet() { var result = this._chandle.useResult(this._conn).result; if (result) { this._chandle.freeResult(result) } } +*/ diff --git a/src/connector/nodejs/nodetaos/cursor.js b/src/connector/nodejs/nodetaos/cursor.js index acfe96dfbcb94f5e12e26f903ae134aa4ad75f9a..0c9214fe32656fdfb7edb6cfc0d0c3587d4f7bb6 100644 --- a/src/connector/nodejs/nodetaos/cursor.js +++ b/src/connector/nodejs/nodetaos/cursor.js @@ -98,7 +98,7 @@ TDengineCursor.prototype.execute = function execute(operation, options, callback if (this._connection == null) { throw new errors.ProgrammingError('Cursor is not connected'); } - this._connection._clearResultSet(); + this._reset_result(); let stmt = operation; @@ -111,18 +111,18 @@ TDengineCursor.prototype.execute = function execute(operation, options, callback }); obs.observe({ entryTypes: ['measure'] }); performance.mark('A'); - res = this._chandle.query(this._connection._conn, stmt); + this._result = this._chandle.query(this._connection._conn, stmt); performance.mark('B'); performance.measure('query', 'A', 'B'); } else { - res = this._chandle.query(this._connection._conn, stmt); + this._result = this._chandle.query(this._connection._conn, stmt); } - + res = this._chandle.errno(this._result); if (res == 0) { - let fieldCount = this._chandle.fieldsCount(this._connection._conn); + let fieldCount = this._chandle.fieldsCount(this._result); if (fieldCount == 0) { - let affectedRowCount = this._chandle.affectedRows(this._connection._conn); + let affectedRowCount = this._chandle.affectedRows(this._result); let response = this._createAffectedResponse(affectedRowCount, time) if (options['quiet'] != true) { console.log(response); @@ -131,16 +131,15 @@ TDengineCursor.prototype.execute = function execute(operation, options, callback return affectedRowCount; //return num of affected rows, common with insert, use statements } else { - let resAndField = this._chandle.useResult(this._connection._conn, fieldCount) - this._result = resAndField.result; - this._fields = resAndField.fields; - this.fields = resAndField.fields; + this._fields = this._chandle.useResult(this._result); + this.fields = this._fields; wrapCB(callback); + return this._result; //return a pointer to the result } } else { - throw new errors.ProgrammingError(this._chandle.errStr(this._connection._conn)) + throw new errors.ProgrammingError(this._chandle.errStr(this._result)) } } @@ -198,18 +197,18 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) { while(true) { let blockAndRows = this._chandle.fetchBlock(this._result, this._fields); - let block = blockAndRows.blocks; let num_of_rows = blockAndRows.num_of_rows; - if (num_of_rows == 0) { break; } this._rowcount += num_of_rows; + let numoffields = this._fields.length; for (let i = 0; i < num_of_rows; i++) { data.push([]); - let rowBlock = new Array(this._fields.length); - for (let j = 0; j < this._fields.length; j++) { + + let rowBlock = new Array(numoffields); + for (let j = 0; j < numoffields; j++) { rowBlock[j] = block[j][i]; } data[data.length-1] = (rowBlock); @@ -221,7 +220,7 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) { let response = this._createSetResponse(this._rowcount, time) console.log(response); - this._connection._clearResultSet(); + // this._connection._clearResultSet(); let fields = this.fields; this._reset_result(); this.data = data; @@ -266,13 +265,15 @@ TDengineCursor.prototype.execute_a = function execute_a (operation, options, cal } if (resCode >= 0) { - let fieldCount = cr._chandle.numFields(res2); - if (fieldCount == 0) { - cr._chandle.freeResult(res2); - } - else { - return res2; - } +// let fieldCount = cr._chandle.numFields(res2); +// if (fieldCount == 0) { +// //cr._chandle.freeResult(res2); +// return res2; +// } +// else { +// return res2; +// } + return res2; } else { @@ -381,6 +382,9 @@ TDengineCursor.prototype.stopQuery = function stopQuery(result) { } TDengineCursor.prototype._reset_result = function _reset_result() { this._rowcount = -1; + if (this._result != null) { + this._chandle.freeResult(this._result); + } this._result = null; this._fields = null; this.data = []; diff --git a/src/connector/nodejs/package-lock.json b/src/connector/nodejs/package-lock.json index 1137e351064e3e2540399e5f23accd2d073fde30..d13fe6959c07955f5b15c7281f571ab932a996c3 100644 --- a/src/connector/nodejs/package-lock.json +++ b/src/connector/nodejs/package-lock.json @@ -1,6 +1,6 @@ { - "name": "td-connector", - "version": "1.6.1", + "name": "td2.0-connector", + "version": "2.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/connector/nodejs/package.json b/src/connector/nodejs/package.json index 2bc4a2453debac241744fe114cb6bae654e882e6..3f0600a09ca73c3fadbed1b7e65c51b30004edf5 100644 --- a/src/connector/nodejs/package.json +++ b/src/connector/nodejs/package.json @@ -1,6 +1,6 @@ { - "name": "td-connector", - "version": "2.0.0", + "name": "td2.0-connector", + "version": "2.0.1", "description": "A Node.js connector for TDengine.", "main": "tdengine.js", "scripts": { diff --git a/src/connector/nodejs/readme.md b/src/connector/nodejs/readme.md index cf60b110248288424a16ab66bc67e5c8f8f94c0f..26a28afbdd514ad97e969302e7d790f6240bb770 100644 --- a/src/connector/nodejs/readme.md +++ b/src/connector/nodejs/readme.md @@ -1,23 +1,24 @@ # TDengine Node.js connector -[![minzip](https://img.shields.io/bundlephobia/minzip/td-connector.svg)](https://github.com/taosdata/TDengine/tree/master/src/connector/nodejs) [![NPM](https://img.shields.io/npm/l/td-connector.svg)](https://github.com/taosdata/TDengine/#what-is-tdengine) +[![minzip](https://img.shields.io/bundlephobia/minzip/td2.0-connector.svg)](https://github.com/taosdata/TDengine/tree/master/src/connector/nodejs) [![NPM](https://img.shields.io/npm/l/td2.0-connector.svg)](https://github.com/taosdata/TDengine/#what-is-tdengine) -This is the Node.js library that lets you connect to [TDengine](https://www.github.com/taosdata/tdengine). It is built so that you can use as much of it as you want or as little of it as you want through providing an extensive API. If you want the raw data in the form of an array of arrays for the row data retrieved from a table, you can do that. If you want to wrap that data with objects that allow you easily manipulate and display data such as using a prettifier function, you can do that! +This is the Node.js library that lets you connect to [TDengine](https://www.github.com/taosdata/tdengine) 2.0 version. It is built so that you can use as much of it as you want or as little of it as you want through providing an extensive API. If you want the raw data in the form of an array of arrays for the row data retrieved from a table, you can do that. If you want to wrap that data with objects that allow you easily manipulate and display data such as using a prettifier function, you can do that! ## Installation To get started, just type in the following to install the connector through [npm](https://www.npmjs.com/) ```cmd -npm install td-connector +npm install td2.0-connector ``` To interact with TDengine, we make use of the [node-gyp](https://github.com/nodejs/node-gyp) library. To install, you will need to install the following depending on platform (the following instructions are quoted from node-gyp) -### On Unix +### On Linux - `python` (`v2.7` recommended, `v3.x.x` is **not** supported) - `make` - A proper C/C++ compiler toolchain, like [GCC](https://gcc.gnu.org) +- `node` (between `v10.x` and `v11.x`, other version has some dependency compatibility problems) ### On macOS @@ -71,12 +72,12 @@ The following is a short summary of the basic usage of the connector, the full ### Connection -To use the connector, first require the library ```td-connector```. Running the function ```taos.connect``` with the connection options passed in as an object will return a TDengine connection object. The required connection option is ```host```, other options if not set, will be the default values as shown below. +To use the connector, first require the library ```td2.0-connector```. Running the function ```taos.connect``` with the connection options passed in as an object will return a TDengine connection object. The required connection option is ```host```, other options if not set, will be the default values as shown below. A cursor also needs to be initialized in order to interact with TDengine from Node.js. ```javascript -const taos = require('td-connector'); +const taos = require('td2.0-connector'); var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0}) var cursor = conn.cursor(); // Initializing a new cursor ``` diff --git a/src/connector/nodejs/test/test.js b/src/connector/nodejs/test/test.js index 5d96e798d8b295dd986eb50de3e7954b923098c9..73dac8b26ce07ff159ce3de535776e2495d1ebde 100644 --- a/src/connector/nodejs/test/test.js +++ b/src/connector/nodejs/test/test.js @@ -19,7 +19,7 @@ function randomBool() { } // Initialize - +//c1.execute('drop database td_connector_test;'); c1.execute('create database if not exists td_connector_test;'); c1.execute('use td_connector_test;') c1.execute('create table if not exists all_types (ts timestamp, _int int, _bigint bigint, _float float, _double double, _binary binary(40), _smallint smallint, _tinyint tinyint, _bool bool, _nchar nchar(40));'); @@ -46,12 +46,14 @@ for (let i = 0; i < 10000; i++) { } // Select -c1.execute('select * from td_connector_test.all_types limit 10 offset 1000;'); +console.log('select * from td_connector_test.all_types limit 3 offset 100;'); +c1.execute('select * from td_connector_test.all_types limit 2 offset 100;'); var d = c1.fetchall(); console.log(c1.fields); console.log(d); // Functions +console.log('select count(*), avg(_int), sum(_float), max(_bigint), min(_double) from td_connector_test.all_types;') c1.execute('select count(*), avg(_int), sum(_float), max(_bigint), min(_double) from td_connector_test.all_types;'); var d = c1.fetchall(); console.log(c1.fields); @@ -62,9 +64,11 @@ console.log(d); c1.query('select count(*), stddev(_double), min(_tinyint) from all_types where _tinyint > 50 and _int < 0;', true).then(function(result){ result.pretty(); }) + c1.query('select _tinyint, _bool from all_types where _tinyint > 50 and _int < 0 limit 50;', true).then(function(result){ result.pretty(); }) + c1.query('select stddev(_double), stddev(_bigint), stddev(_float) from all_types;', true).then(function(result){ result.pretty(); }) @@ -83,54 +87,58 @@ q.execute().then(function(r) { // Raw Async Testing (Callbacks, not promises) function cb2(param, result, rowCount, rd) { + console.log('CB2 Callbacked!'); console.log("RES *", result); - console.log("Async fetched", rowCount, "rows"); + console.log("Async fetched", rowCount, " rows"); console.log("Passed Param: ", param); - console.log("Fields", rd.fields); - console.log("Data", rd.data); - + console.log("Fields ", rd.fields); + console.log("Data ", rd.data); } function cb1(param,result,code) { - console.log('Callbacked!'); - console.log("RES *", result); + console.log('CB1 Callbacked!'); + console.log("RES * ", result); console.log("Status: ", code); - console.log("Passed Param", param); - c1.fetchall_a(result, cb2, param) + console.log("Passed Param ", param); + c1.fetchall_a(result, cb2, param); } c1.execute_a("describe td_connector_test.all_types;", cb1, {myparam:3.141}); function cb4(param, result, rowCount, rd) { + console.log('CB4 Callbacked!'); console.log("RES *", result); console.log("Async fetched", rowCount, "rows"); console.log("Passed Param: ", param); console.log("Fields", rd.fields); console.log("Data", rd.data); - } // Without directly calling fetchall_a var thisRes; function cb3(param,result,code) { - console.log('Callbacked!'); + console.log('CB3 Callbacked!'); console.log("RES *", result); - console.log("Status: ", code); + console.log("Status:", code); console.log("Passed Param", param); thisRes = result; } //Test calling execute and fetchall seperately and not through callbacks var param = c1.execute_a("describe td_connector_test.all_types;", cb3, {e:2.718}); console.log("Passed Param outside of callback: ", param); +console.log(param); setTimeout(function(){ c1.fetchall_a(thisRes, cb4, param); },100); // Async through promises -var aq = c1.query('select count(*) from td_connector_test.all_types;') +var aq = c1.query('select count(*) from td_connector_test.all_types;',false); aq.execute_a().then(function(data) { data.pretty(); -}) +}); c1.query('describe td_connector_test.stabletest;').execute_a().then(r=> r.pretty()); setTimeout(function(){ c1.query('drop database td_connector_test;'); +},200); +setTimeout(function(){ + conn.close(); },2000); -conn.close(); + diff --git a/src/util/tests/CMakeLists.txt b/src/util/tests/CMakeLists.txt index b0b5d3013b250e109f0c5a0ef4fc2c23c4563406..8687a8005ddeda7320c60c9ef90dd221f56b971f 100644 --- a/src/util/tests/CMakeLists.txt +++ b/src/util/tests/CMakeLists.txt @@ -11,5 +11,5 @@ IF (HEADER_GTEST_INCLUDE_DIR AND LIB_GTEST_STATIC_DIR) AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} SOURCE_LIST) ADD_EXECUTABLE(utilTest ${SOURCE_LIST}) - TARGET_LINK_LIBRARIES(utilTest tutil common gtest pthread gcov) + TARGET_LINK_LIBRARIES(utilTest tutil common osdetail gtest pthread gcov) ENDIF()