diff --git a/alert/cmd/alert/install_driver.sh b/alert/cmd/alert/install_driver.sh old mode 100644 new mode 100755 index 1171a9953846475289531c2e07e89e5465c65959..c7d44786ecbe05e9838130d957a913ee19f55dfc --- a/alert/cmd/alert/install_driver.sh +++ b/alert/cmd/alert/install_driver.sh @@ -9,9 +9,7 @@ set -e script_dir=$(dirname $(readlink -f "$0")) # Dynamic directory lib_link_dir="/usr/lib" - -#install main path -install_main_dir="/usr/local/taos" +lib64_link_dir="/usr/lib64" # Color setting RED='\033[0;31m' @@ -25,24 +23,23 @@ if command -v sudo > /dev/null; then csudo="sudo" fi -function clean_driver() { - ${csudo} rm -f /usr/lib/libtaos.so || : -} - function install_driver() { - echo -e "${GREEN}Start to install TDengine client driver ...${NC}" - - #create install main dir and all sub dir - ${csudo} mkdir -p ${install_main_dir} - ${csudo} mkdir -p ${install_main_dir}/driver - - ${csudo} rm -f ${lib_link_dir}/libtaos.* || : - ${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 - echo - echo -e "\033[44;32;1mTDengine client driver is successfully installed!${NC}" + if [[ -d ${lib_link_dir} && ! -e ${lib_link_dir}/libtaos.so ]]; then + echo -e "${GREEN}Start to install TDengine client driver ...${NC}" + ${csudo} ln -s ${script_dir}/driver/libtaos.* ${lib_link_dir}/libtaos.so.1 || : + ${csudo} ln -s ${lib_link_dir}/libtaos.so.1 ${lib_link_dir}/libtaos.so || : + + if [[ -d ${lib64_link_dir} && ! -e ${lib64_link_dir}/libtaos.so ]]; then + ${csudo} ln -s ${script_dir}/driver/libtaos.* ${lib64_link_dir}/libtaos.so.1 || : + ${csudo} ln -s ${lib64_link_dir}/libtaos.so.1 ${lib64_link_dir}/libtaos.so || : + fi + + echo + echo -e "${GREEN}TDengine client driver is successfully installed!${NC}" + else + echo -e "${GREEN}TDengine client driver already exists, Please confirm whether the alert version matches the client driver version!${NC}" + fi } install_driver diff --git a/alert/cmd/alert/main.go b/alert/cmd/alert/main.go index f4c30e156a742c5edc5399dfaf318bedd3ceb1ce..1bd185b580aa3dda1206986cc47ae1151ed41c29 100644 --- a/alert/cmd/alert/main.go +++ b/alert/cmd/alert/main.go @@ -119,7 +119,7 @@ WantedBy=multi-user.target return nil } -const version = "TDengine alert v2.0.0.1" +var version = "2.0.0.1s" func main() { var ( @@ -133,7 +133,7 @@ func main() { flag.Parse() if showVersion { - fmt.Println(version) + fmt.Println("TDengine alert v" + version) return } diff --git a/alert/release.sh b/alert/release.sh index 3342b0e56781408e5f21cf7aebaff0b88f9af0c4..35eb4d677f2a6f07699c2668ea67d44c5af44c92 100755 --- a/alert/release.sh +++ b/alert/release.sh @@ -6,9 +6,9 @@ set -e # set parameters by default value cpuType=amd64 # [armv6l | arm64 | amd64 | 386] osType=linux # [linux | darwin | windows] - +version="" declare -A archMap=(["armv6l"]="arm" ["arm64"]="arm64" ["amd64"]="x64" ["386"]="x86") -while getopts "h:c:o:" arg +while getopts "h:c:o:n:" arg do case $arg in c) @@ -19,6 +19,10 @@ do #echo "osType=$OPTARG" osType=$(echo $OPTARG) ;; + n) + #echo "version=$OPTARG" + version=$(echo $OPTARG) + ;; h) echo "Usage: `basename $0` -c [armv6l | arm64 | amd64 | 386] -o [linux | darwin | windows]" exit 0 @@ -30,18 +34,27 @@ do esac done +if [ "$version" == "" ]; then + echo "Please input the correct version!" + exit 1 +fi startdir=$(pwd) scriptdir=$(dirname $(readlink -f $0)) cd ${scriptdir}/cmd/alert -version=$(grep 'const version =' main.go | awk '{print $NF}') -version=${version%\"} -version=${version:1} echo "cpuType=${cpuType}" echo "osType=${osType}" echo "version=${version}" -GOOS=${osType} GOARCH=${cpuType} go build +GOOS=${osType} GOARCH=${cpuType} go build -ldflags '-X main.version='${version} + +mkdir -p TDengine-alert/driver + +cp alert alert.cfg install_driver.sh ./TDengine-alert/. +cp ../../../debug/build/lib/libtaos.so.${version} ./TDengine-alert/driver/. +chmod 777 ./TDengine-alert/install_driver.sh + +tar -I 'gzip -9' -cf ${startdir}/TDengine-alert-${version}-${osType^}-${archMap[${cpuType}]}.tar.gz TDengine-alert/ +rm -rf ./TDengine-alert -tar -I 'gzip -9' -cf ${startdir}/TDengine-alert-${version}-${osType^}-${archMap[${cpuType}]}.tar.gz alert alert.cfg install_driver.sh driver/ diff --git a/documentation20/webdocs/markdowndocs/Connections with other Tools-ch.md b/documentation20/webdocs/markdowndocs/Connections with other Tools-ch.md index d62a525e284936bc78fadb0c371fce0c218fd638..4e55f3c5e8f12f3277898b1f825dca5e22b2e6d1 100644 --- a/documentation20/webdocs/markdowndocs/Connections with other Tools-ch.md +++ b/documentation20/webdocs/markdowndocs/Connections with other Tools-ch.md @@ -11,7 +11,7 @@ TDengine能够与开源数据可视化系统[Grafana](https://www.grafana.com/) ### 配置Grafana -TDengine的Grafana插件在安装包的/usr/local/taos/connector/grafana目录下。 +TDengine的Grafana插件在安装包的/usr/local/taos/connector/grafanaplugin目录下。 以CentOS 7.2操作系统为例,将tdengine目录拷贝到/var/lib/grafana/plugins目录下,重新启动grafana即可。 diff --git a/documentation20/webdocs/markdowndocs/Taos Error Code-ch.md b/documentation20/webdocs/markdowndocs/Taos Error Code-ch.md index 6ba0decfd36f59c3d91f273c92af7a9e9125c191..a9a3dee432c57b0078948d15799ac29df0167ae2 100644 --- a/documentation20/webdocs/markdowndocs/Taos Error Code-ch.md +++ b/documentation20/webdocs/markdowndocs/Taos Error Code-ch.md @@ -1,7 +1,7 @@ # TDengine 2.0 错误码以及对应的十进制码 -| Code | bit | error code | 错误描述 | 十进制错误码 | +| 状态码 | 模 | 错误码(十六进制) | 错误描述 | 错误码(十进制) | |-----------------------| :---: | :---------: | :------------------------ | ---------------- | |TSDB_CODE_RPC_ACTION_IN_PROGRESS| 0 | 0x0001| "Action in progress"| -2147483647| |TSDB_CODE_RPC_AUTH_REQUIRED| 0 | 0x0002 | "Authentication required"| -2147483646| @@ -87,7 +87,7 @@ |TSDB_CODE_MND_INVALID_ACCT_OPTION| 0 | 0x0342 | "Invalid account options"| -2147482814| |TSDB_CODE_MND_USER_ALREADY_EXIST| 0 | 0x0350 | "User already exists"| -2147482800| |TSDB_CODE_MND_INVALID_USER |0 | 0x0351 | "Invalid user" |-2147482799| -|TSDB_CODE_MND_INVALID_USER_FORMAT| |0 |0x0352 |"Invalid user format" |-2147482798| +|TSDB_CODE_MND_INVALID_USER_FORMAT| 0 |0x0352 |"Invalid user format" |-2147482798| |TSDB_CODE_MND_INVALID_PASS_FORMAT| 0| 0x0353 | "Invalid password format"| -2147482797| |TSDB_CODE_MND_NO_USER_FROM_CONN| 0 | 0x0354 | "Can not get user from conn"| -2147482796| |TSDB_CODE_MND_TOO_MANY_USERS| 0 | 0x0355| "Too many users"| -2147482795| @@ -107,7 +107,7 @@ |TSDB_CODE_MND_DB_NOT_SELECTED| 0 | 0x0380 | "Database not specified or available"| -2147482752| |TSDB_CODE_MND_DB_ALREADY_EXIST| 0 | 0x0381 | "Database already exists"| -2147482751| |TSDB_CODE_MND_INVALID_DB_OPTION| 0 | 0x0382 | "Invalid database options"| -2147482750| -|TSDB_CODE_MND_INVALID_DB| |0 | 0x0383 | "Invalid database name"| -2147482749| +|TSDB_CODE_MND_INVALID_DB| 0 | 0x0383 | "Invalid database name"| -2147482749| |TSDB_CODE_MND_MONITOR_DB_FORBIDDEN| 0 | 0x0384 | "Cannot delete monitor database"| -2147482748| |TSDB_CODE_MND_TOO_MANY_DATABASES| 0| 0x0385 | "Too many databases for account"| -2147482747| |TSDB_CODE_MND_DB_IN_DROPPING| 0 | 0x0386| "Database not available" |-2147482746| diff --git a/documentation20/webdocs/markdowndocs/cluster-ch.md b/documentation20/webdocs/markdowndocs/cluster-ch.md index a1ac1d6fd6047aa6b4b80f41945d67fe505ae9db..193de729ba9ccf9c1efba53520ac2d69f5f51d87 100644 --- a/documentation20/webdocs/markdowndocs/cluster-ch.md +++ b/documentation20/webdocs/markdowndocs/cluster-ch.md @@ -1,48 +1,69 @@ # TDengine 集群安装、管理 -多个taosd的运行实例可以组成一个集群,以保证TDengine的高可靠运行,并提供水平扩展能力。要了解TDengine 2.0的集群管理,需要对集群的基本概念有所了解,请看TDengine 2.0整体架构一章。而且在安装集群之前,请按照[《立即开始》](https://www.taosdata.com/cn/getting-started20/)一章安装并体验过单节点功能。 +多个TDengine服务器,也就是多个taosd的运行实例可以组成一个集群,以保证TDengine的高可靠运行,并提供水平扩展能力。要了解TDengine 2.0的集群管理,需要对集群的基本概念有所了解,请看TDengine 2.0整体架构一章。而且在安装集群之前,先请按照[《立即开始》](https://www.taosdata.com/cn/getting-started20/)一章安装并体验单节点功能。 -集群的每个节点是由End Point来唯一标识的,End Point是由FQDN(Fully Qualified Domain Name)外加Port组成,比如 h1.taosdata.com:6030。一般FQDN就是服务器的hostname,可通过Linux命令`hostname -f`获取,FQDN配置参考:[一篇文章说清楚TDengine的FQDN](https://www.taosdata.com/blog/2020/09/11/1824.html)。端口是这个节点对外服务的端口号,缺省是6030,但可以通过taos.cfg里配置参数serverPort进行修改。一个节点可能配置了多个hostname, TDengine会自动获取第一个,但也可以通过taos.cfg里配置参数fqdn进行指定。如果习惯IP地址直接访问,可以将参数fqdn设置为本节点的IP地址。 +集群的每个数据节点是由End Point来唯一标识的,End Point是由FQDN(Fully Qualified Domain Name)外加Port组成,比如 h1.taosdata.com:6030。一般FQDN就是服务器的hostname,可通过Linux命令`hostname -f`获取,FQDN配置参考:[一篇文章说清楚TDengine的FQDN](https://www.taosdata.com/blog/2020/09/11/1824.html)。端口是这个数据节点对外服务的端口号,缺省是6030,但可以通过taos.cfg里配置参数serverPort进行修改。一个物理节点可能配置了多个hostname, TDengine会自动获取第一个,但也可以通过taos.cfg里配置参数fqdn进行指定。如果习惯IP地址直接访问,可以将参数fqdn设置为本节点的IP地址。 TDengine的集群管理极其简单,除添加和删除节点需要人工干预之外,其他全部是自动完成,最大程度的降低了运维的工作量。本章对集群管理的操作做详细的描述。 ## 准备工作 -**第一步**:如果搭建集群的节点中,存有之前的测试数据、装过1.X的版本,或者装过其他版本的TDengine,请先将其删除,并清空所有数据,具体步骤请参考博客[《TDengine多种安装包的安装和卸载》](https://www.taosdata.com/blog/2019/08/09/566.html ) +**第零步**:规划集群所有物理节点的FQDN,将规划好的FQDN分别添加到每个物理节点的/etc/hostname;修改每个物理节点的/etc/hosts,将所有集群物理节点的IP与FQDN的对应添加好【如部署了DNS,请联系网络管理员在DNS上做好相关配置】; + +**第一步**:如果搭建集群的物理节点中,存有之前的测试数据、装过1.X的版本,或者装过其他版本的TDengine,请先将其删除,并清空所有数据,具体步骤请参考博客[《TDengine多种安装包的安装和卸载》](https://www.taosdata.com/blog/2019/08/09/566.html ) **注意1:**因为FQDN的信息会写进文件,如果之前没有配置或者更改FQDN,且启动了TDengine。请一定在确保数据无用或者备份的前提下,清理一下之前的数据(rm -rf /var/lib/taos/); **注意2:**客户端也需要配置,确保它可以正确解析每个节点的FQDN配置,不管是通过DNS服务,还是 Host 文件。 -**第二步**:建议关闭防火墙,至少保证端口:6030 - 6042的TCP和UDP端口都是开放的。**强烈建议**先关闭防火墙,集群搭建完毕之后,再来配置端口; +**第二步**:建议关闭所有物理节点的防火墙,至少保证端口:6030 - 6042的TCP和UDP端口都是开放的。**强烈建议**先关闭防火墙,集群搭建完毕之后,再来配置端口; -**第三步**:在所有节点安装TDengine,且版本必须是一致的,**但不要启动taosd**; +**第三步**:在所有节点安装TDengine,且版本必须是一致的,**但不要启动taosd**。安装时,提示输入是否要加入一个已经存在的TDengine集群时,第一个物理节点直接回车创建新集群,后续物理节点则输入该集群任何一个在线的物理节点的FQDN:端口号(默认6030); -**第四步**:检查、配置所有节点的FQDN: +**第四步**:检查所有数据节点,以及应用所在物理节点的网络设置: -1. 每个节点上执行命令`hostname -f`,查看和确认所有节点的hostname是不相同的; -2. 每个节点上执行`ping host`, 其中host是其他节点的hostname, 看能否ping通其它节点; 如果不能ping通,需要检查网络设置, 或/etc/hosts文件,或DNS的配置。如果无法ping通,是无法组成集群的。 -3. 每个节点的FQDN就是输出的hostname外加端口号,比如h1.taosdata.com:6030 +1. 每个物理节点上执行命令`hostname -f`,查看和确认所有节点的hostname是不相同的(应用驱动所在节点无需做此项检查); +2. 每个物理节点上执行`ping host`, 其中host是其他物理节点的hostname, 看能否ping通其它物理节点; 如果不能ping通,需要检查网络设置, 或/etc/hosts文件(Windows系统默认路径为C:\Windows\system32\drivers\etc\hosts),或DNS的配置。如果无法ping通,是无法组成集群的; +3. 每个数据节点的End Point就是输出的hostname外加端口号,比如h1.taosdata.com:6030 -**第五步**:修改TDengine的配置文件(所有节点的文件/etc/taos/taos.cfg都需要修改)。假设准备启动的第一个节点End Point为 h1.taosdata.com:6030, 那么以下几个参数与集群相关: +**第五步**:修改TDengine的配置文件(所有节点的文件/etc/taos/taos.cfg都需要修改)。假设准备启动的第一个数据节点End Point为 h1.taosdata.com:6030, 其与集群配置相关参数如下: ``` -// firstEp 集群中所有节点的配置都是一致的,对其第一次访问后,就获得了整个集群的信息 +// firstEp 是每个数据节点首次启动后连接的第一个数据节点 firstEp h1.taosdata.com:6030 -// 配置本节点的FQDN,如果本机只有一个hostname, 无需配置 +// 配置本数据节点的FQDN,如果本机只有一个hostname, 无需配置 fqdn h1.taosdata.com -// 配置本节点的端口号,缺省是6030 +// 配置本数据节点的端口号,缺省是6030 serverPort 6030 // 服务端节点数为偶数的时候,需要配置,请参考《Arbitrator的使用》的部分 arbitrator ha.taosdata.com:6042 ``` -一定要修改的参数是firstEp, 其他参数可不做任何修改,除非你很清楚为什么要修改。 +一定要修改的参数是firstEp和fqdn, 其他参数可不做任何修改,除非你很清楚为什么要修改。 + +**加入到集群中的数据节点dnode,涉及集群相关的下表11项参数必须完全相同,否则不能成功加入到集群中。** + +| **#** | **配置参数名称** | **含义** | +| ----- | ------------------ | ---------------------------------------- | +| 1 | numOfMnodes | 系统中管理节点个数 | +| 2 | mnodeEqualVnodeNum | 一个mnode等同于vnode消耗的个数 | +| 3 | offlineThreshold | dnode离线阈值,超过该时间将导致Dnode离线 | +| 4 | statusInterval | dnode向mnode报告状态时长 | +| 5 | arbitrator | 系统中裁决器的end point | +| 6 | timezone | 时区 | +| 7 | locale | 系统区位信息及编码格式 | +| 8 | charset | 字符集编码 | +| 9 | balance | 是否启动负载均衡 | +| 10 | maxTablesPerVnode | 每个vnode中能够创建的最大表个数 | +| 11 | maxVgroupsPerDb | 每个DB中 能够使用的最大vnode个数 | + + -## 启动第一个节点 +## 启动第一个数据节点 + +按照[《立即开始》](https://www.taosdata.com/cn/getting-started20/)里的指示,启动第一个数据节点,例如h1.taosdata.com,然后执行taos, 启动taos shell,从shell里执行命令"show dnodes;",如下所示: -按照[《立即开始》](https://www.taosdata.com/cn/getting-started20/)里的指示,启动第一个节点h1.taosdata.com,然后执行taos, 启动taos shell,从shell里执行命令"show dnodes;",如下所示: ``` Welcome to the TDengine shell from Linux, Client Version:2.0.0.0 Copyright (c) 2017 by TAOS Data, Inc. All rights reserved. @@ -55,74 +76,86 @@ Query OK, 1 row(s) in set (0.006385s) taos> ``` -上述命令里,可以看到这个刚启动的这个节点的End Point是:h1.taos.com:6030 -## 启动后续节点 +上述命令里,可以看到这个刚启动的这个数据节点的End Point是:h1.taos.com:6030,就是这个新集群的firstEP。 + +## 启动后续数据节点 -将后续的节点添加到现有集群,具体有以下几步: +将后续的数据节点添加到现有集群,具体有以下几步: -1. 按照["立即开始“](https://www.taosdata.com/cn/getting-started/)一章的方法在每个节点启动taosd。 +1. 按照["立即开始“](https://www.taosdata.com/cn/getting-started/)一章的方法在每个物理节点启动taosd; -2. 在第一个节点,使用CLI程序taos, 登录进TDengine系统, 执行命令: +2. 在第一个数据节点,使用CLI程序taos, 登录进TDengine系统, 执行命令: - ``` - CREATE DNODE "h2.taos.com:6030"; - ``` + ``` + CREATE DNODE "h2.taos.com:6030"; + ``` - 将新节点的End Point (准备工作中第四步获知的) 添加进集群的EP列表。**"fqdn:port"需要用双引号引起来**,否则出错。请注意将示例的“h2.taos.com:6030" 替换为这个新节点的End Point。 + 将新数据节点的End Point (准备工作中第四步获知的) 添加进集群的EP列表。**"fqdn:port"需要用双引号引起来**,否则出错。请注意将示例的“h2.taos.com:6030" 替换为这个新数据节点的End Point。 3. 然后执行命令 - ``` - SHOW DNODES; - ``` + ``` + SHOW DNODES; + ``` - 查看新节点是否被成功加入。如果该被加入的节点处于离线状态,请做两个检查 - - - 查看该节点的taosd是否正常工作,如果没有正常运行,需要先检查为什么 - - 查看该节点taosd日志文件taosdlog.0里前面几行日志(一般在/var/log/taos目录),看日志里输出的该节点fqdn以及端口号是否为刚添加的End Point。如果不一致,需要将正确的End Point添加进去。 + 查看新节点是否被成功加入。如果该被加入的数据节点处于离线状态,请做两个检查 -按照上述步骤可以源源不断的将新的节点加入到集群。 + - 查看该数据节点的taosd是否正常工作,如果没有正常运行,需要先检查为什么 + - 查看该数据节点taosd日志文件taosdlog.0里前面几行日志(一般在/var/log/taos目录),看日志里输出的该数据节点fqdn以及端口号是否为刚添加的End Point。如果不一致,需要将正确的End Point添加进去。 + +按照上述步骤可以源源不断的将新的数据节点加入到集群。 **提示:** -- firstEp这个参数仅仅在该节点第一次加入集群时有作用,加入集群后,该节点会保存最新的mnode的End Point列表,不再依赖这两个参数。 -- 两个没有配置firstEp参数的dnode启动后,会独立运行起来。这个时候,无法将其中一个节点加入到另外一个节点,形成集群。**无法将两个独立的集群合并成为新的集群**。 +- 任何已经加入集群在线的数据节点,都可以作为后续待加入节点的firstEP。 +- firstEp这个参数仅仅在该数据节点首次加入集群时有作用,加入集群后,该数据节点会保存最新的mnode的End Point列表,不再依赖这个参数。 +- 两个没有配置firstEp参数的数据节点dnode启动后,会独立运行起来。这个时候,无法将其中一个数据节点加入到另外一个数据节点,形成集群。**无法将两个独立的集群合并成为新的集群**。 + +## 数据节点管理 -## 节点管理 +### 添加数据节点 -### 添加节点 执行CLI程序taos, 使用root账号登录进系统, 执行: + ``` CREATE DNODE "fqdn:port"; ``` -将新节点的End Point添加进集群的EP列表。**"fqdn:port"需要用双引号引起来**,否则出错。一个节点对外服务的fqdn和port可以通过配置文件taos.cfg进行配置,缺省是自动获取。 -### 删除节点 +将新数据节点的End Point添加进集群的EP列表。**"fqdn:port"需要用双引号引起来**,否则出错。一个数据节点对外服务的fqdn和port可以通过配置文件taos.cfg进行配置,缺省是自动获取。【强烈不建议用自动获取方式来配置FQDN,可能导致生成的数据节点的End Point不是所期望的】 + +### 删除数据节点 + 执行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数目等信息。在添加或删除一个节点后,可以使用该命令查看。 + +它将列出集群中所有的dnode,每个dnode的fqdn:port, 状态(ready, offline等),vnode数目,还未使用的vnode数目等信息。在添加或删除一个数据节点后,可以使用该命令查看。 ### 查看虚拟节点组 -为充分利用多核技术,并提供scalability,数据需要分片处理。因此TDengine会将一个DB的数据切分成多份,存放在多个vnode里。这些vnode可能分布在多个dnode里,这样就实现了水平扩展。一个vnode仅仅属于一个DB,但一个DB可以有多个vnode。vnode的是mnode根据当前系统资源的情况,自动进行分配的,无需任何人工干预。 +为充分利用多核技术,并提供scalability,数据需要分片处理。因此TDengine会将一个DB的数据切分成多份,存放在多个vnode里。这些vnode可能分布在多个数据节点dnode里,这样就实现了水平扩展。一个vnode仅仅属于一个DB,但一个DB可以有多个vnode。vnode的是mnode根据当前系统资源的情况,自动进行分配的,无需任何人工干预。 执行CLI程序taos,使用root账号登录进TDengine系统,执行: + ``` SHOW VGROUPS; ``` + ## vnode的高可用性 + TDengine通过多副本的机制来提供系统的高可用性,包括vnode和mnode的高可用性。 vnode的副本数是与DB关联的,一个集群里可以有多个DB,根据运营的需求,每个DB可以配置不同的副本数。创建数据库时,通过参数replica 指定副本数(缺省为1)。如果副本数为1,系统的可靠性无法保证,只要数据所在的节点宕机,就将无法提供服务。集群的节点数必须大于等于副本数,否则创建表时将返回错误“more dnodes are needed"。比如下面的命令将创建副本数为3的数据库demo: @@ -130,21 +163,25 @@ vnode的副本数是与DB关联的,一个集群里可以有多个DB,根据 ``` 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的一部分表的读写操作。 +一个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的一部分表的读写操作。 -因为vnode的引入,无法简单的给出结论:“集群中过半dnode工作,集群就应该工作”。但是对于简单的情形,很好下结论。比如副本数为3,只有三个dnode,那如果仅有一个节点不工作,整个集群还是可以正常工作的,但如果有两个节点不工作,那整个集群就无法正常工作了。 +因为vnode的引入,无法简单的给出结论:“集群中过半数据节点dnode工作,集群就应该工作”。但是对于简单的情形,很好下结论。比如副本数为3,只有三个dnode,那如果仅有一个节点不工作,整个集群还是可以正常工作的,但如果有两个数据节点不工作,那整个集群就无法正常工作了。 ## Mnode的高可用性 -TDengine集群是由mnode (taosd的一个模块,逻辑节点) 负责管理的,为保证mnode的高可用,可以配置多个mnode副本,副本数由系统配置参数numOfMnodes决定,有效范围为1-3。为保证元数据的强一致性,mnode副本之间是通过同步的方式进行数据复制的。 -一个集群有多个dnode, 但一个dnode至多运行一个mnode实例。多个dnode情况下,哪个dnode可以作为mnode呢?这是完全由系统根据整个系统资源情况,自动指定的。用户可通过CLI程序taos,在TDengine的console里,执行如下命令: +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实例,否则该数据节点dnode无法正常工作,因为一个系统是必须有至少一个mnode的。如果numOfMnodes配置为2,启动第二个dnode时,该dnode也将运行一个mnode实例。 为保证mnode服务的高可用性,numOfMnodes必须设置为2或更大。因为mnode保存的元数据必须是强一致的,如果numOfMnodes大于2,复制参数quorum自动设为2,也就是说,至少要保证有两个副本写入数据成功,才通知客户端应用写入成功。 @@ -154,22 +191,25 @@ SHOW MNODES; 有三种情况,将触发负载均衡,而且都无需人工干预。 -- 当一个新节点添加进集群时,系统将自动触发负载均衡,一些节点上的数据将被自动转移到新节点上,无需任何人工干预。 -- 当一个节点从集群中移除时,系统将自动把该节点上的数据转移到其他节点,无需任何人工干预。 -- 如果一个节点过热(数据量过大),系统将自动进行负载均衡,将该节点的一些vnode自动挪到其他节点。 +- 当一个新数据节点添加进集群时,系统将自动触发负载均衡,一些节点上的数据将被自动转移到新数据节点上,无需任何人工干预。 +- 当一个数据节点从集群中移除时,系统将自动把该数据节点上的数据转移到其他数据节点,无需任何人工干预。 +- 如果一个数据节点过热(数据量过大),系统将自动进行负载均衡,将该数据节点的一些vnode自动挪到其他节点。 + +当上述三种情况发生时,系统将启动一各个数据节点的负载计算,从而决定如何挪动。 -当上述三种情况发生时,系统将启动一各个节点的负载计算,从而决定如何挪动。 +**【提示】负载均衡由参数balance控制,它决定是否启动自动负载均衡。** -##节点离线处理 -如果一个节点离线,TDengine集群将自动检测到。有如下两种情况: -- 改节点离线超过一定时间(taos.cfg里配置参数offlineThreshold控制时长),系统将自动把该节点删除,产生系统报警信息,触发负载均衡流程。如果该被删除的节点重现上线时,它将无法加入集群,需要系统管理员重新将其添加进集群才会开始工作。 +## 数据节点离线处理 + +如果一个数据节点离线,TDengine集群将自动检测到。有如下两种情况: + +- 该数据节点离线超过一定时间(taos.cfg里配置参数offlineThreshold控制时长),系统将自动把该数据节点删除,产生系统报警信息,触发负载均衡流程。如果该被删除的数据节点重现上线时,它将无法加入集群,需要系统管理员重新将其添加进集群才会开始工作。 - 离线后,在offlineThreshold的时长内重新上线,系统将自动启动数据恢复流程,等数据完全恢复后,该节点将开始正常工作。 -**注意:**如果一个虚拟节点组(包括mnode组)里每个节点都处于离线或unsynced状态,必须等该虚拟节点组里的所有节点都上线、都能交换状态信息后,才能选出Master,该虚拟节点组才能对外提供服务。比如整个集群有3个节点,副本数为3,如果3个节点都宕机,然后2个节点重启,是无法工作的,只有等3个节点都重启成功,才能对外服务。 +**注意:**如果一个虚拟节点组(包括mnode组)里所归属的每个数据节点都处于离线或unsynced状态,必须等该虚拟节点组里的所有数据节点都上线、都能交换状态信息后,才能选出Master,该虚拟节点组才能对外提供服务。比如整个集群有3个数据节点,副本数为3,如果3个数据节点都宕机,然后2个数据节点重启,是无法工作的,只有等3个数据节点都重启成功,才能对外服务。 ## 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`可以指定其对外服务的端口号,缺省是6042。配置每个taosd实例时,可以在配置文件taos.cfg里将参数arbitrator设置为arbitrator的End Point。如果该参数配置了,当副本数为偶数数,系统将自动连接配置的arbitrator。 - diff --git a/src/client/inc/tsclient.h b/src/client/inc/tsclient.h index 74a0e8c11cdfe70584ec99a4d1061acc0c1eeac3..952dcde96226552e77ae9673f538d4a12db72d42 100644 --- a/src/client/inc/tsclient.h +++ b/src/client/inc/tsclient.h @@ -334,7 +334,7 @@ typedef struct STscObj { struct SSqlStream *streamList; void* pDnodeConn; pthread_mutex_t mutex; - T_REF_DECLARE(); + T_REF_DECLARE() } STscObj; typedef struct SSqlObj { @@ -470,7 +470,7 @@ static FORCE_INLINE void tscGetResultColumnChr(SSqlRes* pRes, SFieldInfo* pField int32_t type = pInfo->pSqlExpr->resType; int32_t bytes = pInfo->pSqlExpr->resBytes; - char* pData = pRes->data + pInfo->pSqlExpr->offset * pRes->numOfRows + bytes * pRes->row; + char* pData = pRes->data + (int32_t)(pInfo->pSqlExpr->offset * pRes->numOfRows + bytes * pRes->row); // user defined constant value output columns if (TSDB_COL_IS_UD_COL(pInfo->pSqlExpr->colInfo.flag)) { diff --git a/src/client/src/tscFunctionImpl.c b/src/client/src/tscFunctionImpl.c index da575b9354be32c2a79d46ed204df1a860e9d69a..d8ac0d3109fc207278882e365724879a8114897a 100644 --- a/src/client/src/tscFunctionImpl.c +++ b/src/client/src/tscFunctionImpl.c @@ -2460,11 +2460,11 @@ static void percentile_function(SQLFunctionCtx *pCtx) { if (pInfo->stage == 0) { if (pCtx->preAggVals.isSet) { if (pInfo->minval > pCtx->preAggVals.statis.min) { - pInfo->minval = pCtx->preAggVals.statis.min; + pInfo->minval = (double)pCtx->preAggVals.statis.min; } if (pInfo->maxval < pCtx->preAggVals.statis.max) { - pInfo->maxval = pCtx->preAggVals.statis.max; + pInfo->maxval = (double)pCtx->preAggVals.statis.max; } pInfo->numOfElems += (pCtx->size - pCtx->preAggVals.statis.numOfNull); diff --git a/src/client/src/tscSubquery.c b/src/client/src/tscSubquery.c index 4246abf52d5b5d4dee23467923329e1bd0e556be..10820d8c574a35f5c44c9669fb7b477cd651ca71 100644 --- a/src/client/src/tscSubquery.c +++ b/src/client/src/tscSubquery.c @@ -1950,7 +1950,7 @@ int32_t tscHandleMultivnodeInsert(SSqlObj *pSql) { SSqlCmd *pCmd = &pSql->cmd; SSqlRes *pRes = &pSql->res; - pSql->numOfSubs = taosArrayGetSize(pCmd->pDataBlocks); + pSql->numOfSubs = (uint16_t)taosArrayGetSize(pCmd->pDataBlocks); assert(pSql->numOfSubs > 0); pRes->code = TSDB_CODE_SUCCESS; diff --git a/src/common/src/tglobal.c b/src/common/src/tglobal.c index 96e8fb26c65bc0c13a03d3d9b9c064d57c0fb45e..7e46f58a930bbdac64ea98758a3d4a997fd1a7ec 100644 --- a/src/common/src/tglobal.c +++ b/src/common/src/tglobal.c @@ -957,17 +957,6 @@ static void doInitGlobalConfig(void) { cfg.unitType = TAOS_CFG_UTYPE_NONE; taosInitConfigOption(cfg); - // http configs - cfg.option = "httpCacheSessions"; - cfg.ptr = &tsHttpCacheSessions; - cfg.valType = TAOS_CFG_VTYPE_INT32; - cfg.cfgType = TSDB_CFG_CTYPE_B_CONFIG; - cfg.minValue = 1; - cfg.maxValue = 100000; - cfg.ptrLength = 0; - cfg.unitType = TAOS_CFG_UTYPE_NONE; - taosInitConfigOption(cfg); - cfg.option = "httpEnableRecordSql"; cfg.ptr = &tsHttpEnableRecordSql; cfg.valType = TAOS_CFG_VTYPE_INT32; diff --git a/src/kit/shell/src/shellEngine.c b/src/kit/shell/src/shellEngine.c index 277dc45f8e411a60dcd29b8d072a5e83bdc2146b..1d1ca1c42bbb921adef67b63511d4c70be8ccf2e 100644 --- a/src/kit/shell/src/shellEngine.c +++ b/src/kit/shell/src/shellEngine.c @@ -765,7 +765,9 @@ void read_history() { FILE *f = fopen(f_history, "r"); if (f == NULL) { #ifndef WINDOWS - fprintf(stderr, "Failed to open file %s\n", f_history); + if (errno != ENOENT) { + fprintf(stderr, "Failed to open file %s, reason:%s\n", f_history, strerror(errno)); + } #endif return; } @@ -792,7 +794,7 @@ void write_history() { FILE *f = fopen(f_history, "w"); if (f == NULL) { #ifndef WINDOWS - fprintf(stderr, "Failed to open file %s for write\n", f_history); + fprintf(stderr, "Failed to open file %s for write, reason:%s\n", f_history, strerror(errno)); #endif return; } diff --git a/src/plugins/http/src/httpContext.c b/src/plugins/http/src/httpContext.c index c0c0c494de192b09ea3ffd6620ce1c8a655723fa..92388693753986b0241e03580c3d5e0518421469 100644 --- a/src/plugins/http/src/httpContext.c +++ b/src/plugins/http/src/httpContext.c @@ -131,8 +131,6 @@ HttpContext *httpCreateContext(int32_t fd) { HttpContext *httpGetContext(void *ptr) { uint64_t handleVal = (uint64_t)ptr; HttpContext **ppContext = taosCacheAcquireByKey(tsHttpServer.contextCache, &handleVal, sizeof(HttpContext *)); - ASSERT(ppContext); - ASSERT(*ppContext); if (ppContext) { HttpContext *pContext = *ppContext; diff --git a/src/plugins/http/src/httpRestJson.c b/src/plugins/http/src/httpRestJson.c index 26f04415196a0f93c8fe893ce71a7d704949923c..f8912331a367d2f5fe0e5b659bd6b4a4ad1f43ae 100644 --- a/src/plugins/http/src/httpRestJson.c +++ b/src/plugins/http/src/httpRestJson.c @@ -87,15 +87,12 @@ bool restBuildSqlJson(HttpContext *pContext, HttpSqlCmd *cmd, TAOS_RES *result, JsonBuf *jsonBuf = httpMallocJsonBuf(pContext); if (jsonBuf == NULL) return false; - cmd->numOfRows += numOfRows; - int32_t num_fields = taos_num_fields(result); TAOS_FIELD *fields = taos_fetch_fields(result); for (int32_t k = 0; k < numOfRows; ++k) { TAOS_ROW row = taos_fetch_row(result); if (row == NULL) { - cmd->numOfRows--; continue; } int32_t* length = taos_fetch_lengths(result); @@ -151,24 +148,23 @@ bool restBuildSqlJson(HttpContext *pContext, HttpSqlCmd *cmd, TAOS_RES *result, } // data row array end - httpJsonToken(jsonBuf, JsonArrEnd); - } + httpJsonToken(jsonBuf, JsonArrEnd); + cmd->numOfRows ++; - if (cmd->numOfRows >= tsRestRowLimit) { - httpDebug("context:%p, fd:%d, user:%s, retrieve rows:%d larger than limit:%d, abort retrieve", pContext, - pContext->fd, pContext->user, cmd->numOfRows, tsRestRowLimit); - return false; - } else { if (pContext->fd <= 0) { - httpError("context:%p, fd:%d, user:%s, connection is closed, abort retrieve", pContext, pContext->fd, - pContext->user); + httpError("context:%p, fd:%d, user:%s, conn closed, abort retrieve", pContext, pContext->fd, pContext->user); + return false; + } + + if (cmd->numOfRows >= tsRestRowLimit) { + httpDebug("context:%p, fd:%d, user:%s, retrieve rows:%d larger than limit:%d, abort retrieve", pContext, + pContext->fd, pContext->user, cmd->numOfRows, tsRestRowLimit); return false; - } else { - httpDebug("context:%p, fd:%d, user:%s, total rows:%d retrieved", pContext, pContext->fd, pContext->user, - cmd->numOfRows); - return true; } } + + httpDebug("context:%p, fd:%d, user:%s, retrieved row:%d", pContext, pContext->fd, pContext->user, cmd->numOfRows); + return true; } bool restBuildSqlTimestampJson(HttpContext *pContext, HttpSqlCmd *cmd, TAOS_RES *result, int32_t numOfRows) { diff --git a/src/plugins/http/src/httpServer.c b/src/plugins/http/src/httpServer.c index d85a236cb16b0fe83924ab4affe81287cf9c9475..f0a7249b512d2c9161592abe91822aefba786c20 100644 --- a/src/plugins/http/src/httpServer.c +++ b/src/plugins/http/src/httpServer.c @@ -315,45 +315,48 @@ static bool httpReadData(HttpContext *pContext) { pContext->accessTimes++; pContext->lastAccessTime = taosGetTimestampSec(); + char buf[HTTP_STEP_SIZE + 1] = {0}; - char buf[HTTP_STEP_SIZE + 1] = {0}; - int32_t nread = (int32_t)taosReadSocket(pContext->fd, buf, HTTP_STEP_SIZE); - if (nread > 0) { - buf[nread] = '\0'; - httpTraceL("context:%p, fd:%d, nread:%d content:%s", pContext, pContext->fd, nread, buf); - int32_t ok = httpParseBuf(pParser, buf, nread); - - if (ok) { - httpError("context:%p, fd:%d, parse failed, ret:%d code:%d close connect", pContext, pContext->fd, ok, pParser->parseCode); - httpSendErrorResp(pContext, pParser->parseCode); - httpNotifyContextClose(pContext); - return false; - } + while (1) { + int32_t nread = (int32_t)taosReadSocket(pContext->fd, buf, HTTP_STEP_SIZE); + if (nread > 0) { + buf[nread] = '\0'; + httpTraceL("context:%p, fd:%d, nread:%d content:%s", pContext, pContext->fd, nread, buf); + int32_t ok = httpParseBuf(pParser, buf, nread); + + if (ok) { + httpError("context:%p, fd:%d, parse failed, ret:%d code:%d close connect", pContext, pContext->fd, ok, + pParser->parseCode); + httpSendErrorResp(pContext, pParser->parseCode); + httpNotifyContextClose(pContext); + return false; + } - if (pParser->parseCode) { - httpError("context:%p, fd:%d, parse failed, code:%d close connect", pContext, pContext->fd, pParser->parseCode); - httpSendErrorResp(pContext, pParser->parseCode); - httpNotifyContextClose(pContext); - return false; - } + if (pParser->parseCode) { + httpError("context:%p, fd:%d, parse failed, code:%d close connect", pContext, pContext->fd, pParser->parseCode); + httpSendErrorResp(pContext, pParser->parseCode); + httpNotifyContextClose(pContext); + return false; + } - if (!pParser->parsed) { - httpTrace("context:%p, fd:%d, read not over yet, len:%d", pContext, pContext->fd, pParser->body.pos); - return false; - } else { - httpDebug("context:%p, fd:%d, totalLen:%d", pContext, pContext->fd, pParser->body.pos); - return true; - } - } else if (nread < 0) { - if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { - httpDebug("context:%p, fd:%d, read from socket error:%d, wait another event", pContext, pContext->fd, errno); - return false; // later again + if (!pParser->parsed) { + httpTrace("context:%p, fd:%d, read not finished", pContext, pContext->fd); + continue; + } else { + httpDebug("context:%p, fd:%d, bodyLen:%d", pContext, pContext->fd, pParser->body.pos); + return true; + } + } else if (nread < 0) { + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { + httpDebug("context:%p, fd:%d, read from socket error:%d, wait another event", pContext, pContext->fd, errno); + return false; // later again + } else { + httpError("context:%p, fd:%d, read from socket error:%d, close connect", pContext, pContext->fd, errno); + return false; + } } else { - httpError("context:%p, fd:%d, read from socket error:%d, close connect", pContext, pContext->fd, errno); + httpError("context:%p, fd:%d, nread:%d, wait another event", pContext, pContext->fd, nread); return false; } - } else { - httpError("context:%p, fd:%d, nread:%d, wait another event", pContext, pContext->fd, nread); - return false; } } diff --git a/src/util/src/tlog.c b/src/util/src/tlog.c index a8587de767bc4093a935ca3082a6fe65bbe74e44..e5afe1b68e789e4c5500aad21ea3f074302bc36f 100644 --- a/src/util/src/tlog.c +++ b/src/util/src/tlog.c @@ -433,7 +433,7 @@ void taosPrintLongString(const char *flags, int32_t dflag, const char *format, . va_list argpointer; char buffer[MAX_LOGLINE_DUMP_BUFFER_SIZE]; - int32_t len; + int32_t len; struct tm Tm, *ptm; struct timeval timeSecs; time_t curTime;