此外,该工具支持在 BI 和可视化工具中展示数据,实现远程监控、实时报警和可预测性维护等功能。用户还可以通过微信小程序直接查看设备的运行状态和生成报表。因此,taosX 不仅提升了企业的操作效率,还大幅简化了数据管理流程,助力企业更专注于核心业务的创新与发展,自推出以来,便获得了众多企业客户的关注。
在本篇文章中,我们将深入探讨 taosX 在持续集成(CI)流程优化以及其具体应用场景中的实践。在本次 CI 流程优化后,taosX 的工程质量得到了大幅提升,助力企业能够更有效地利用其数据资源。
在优化之前,taosX 的 CI 流程是基于 Jenkins 构建的。具体来说,当 taosX/Explorer 的 main/3.0 分支有代码合并时,通过在 GitHub 仓库中配置的 webhook 自动触发编译和部署的 Jenkins 作业。该作业完成后,根据 Jenkins 作业之间配置的依赖关系,可以自动或手动触发自动化测试用例的执行作业。测试用例是通过 pytest 工具驱动的,执行完毕后会生成 Allure 测试报告。这一流程确保了代码变更的连续集成和质量控制。
为解决 CI 流程触发滞后性等问题,loveini 研发团队决定采用 Github 提供的 CI/CD 米兰app官方正版下载 Github Action 对 taosX 的 CI 流程进行优化。
在使用 Github Action 需要确定 workflow 中任务执行所使用的 runner。所谓 runner 就是可以运行 Github Action 中的任务的机器。在 runner 中可以执行仓库 clone,软件的安装等任务。Github 默认提供了 runner 来执行 Action 中的任务,这些 runner 其实是 Github 安装好的虚拟机,这些 runner 也被称为 Github-hosted runner。有 Github 提供的机器,相对的也可以用用户自己的机器来运行 workflow。这些机器作为 runner 时被称为 Self-hosted runner。在使用 Github-hosted runner 的时候无法自定义硬件资源(比如 cpu 核数,内存等)。而 Self-hosted runner 则可以控制硬件资源,操作系统以及安装的各个软件。
在 taosx 运行的过程中由于需要加快 CI 流程的运行,需要一个硬件配置较高的设备(taosx 在编译的时候耗时较长,实际测试中发现如果使用 2c4g 的虚拟机进行编译的话耗时在 20 分钟左右,而使用 40c256g 的物理机编译时,耗时在 3 分钟左右)。在执行端到端测试的时候可以提前将测试代码 clone 到一个固定位置,每次只需要 git pull 进行更新,这样也会节省代码拉取的时间。鉴于以上因素,在执行 taosx CI 的是时候选用了 Self-hosted runner。
注意:Self-hosted runner 并不局限在物理机或虚拟机上,可以是一个容器,也可以部署到云端。
Github-hosted runner 与 Self-hosted runner 的详细区别见 Differences between GitHub-hosted and self-hosted runners。
在确定使用 Self-hosted runner 之后,就需要对现有的流程所使用的 workflow 文件进行编排,taosX CI 完整的 workflow 主要包含以下环节:

此流程是通过编写 YAML 格式的 workflow 文件来定义的,该文件需要放在 taosX 代码仓库的.github/workflows目录下。其定义分为以下几个部分:
# 名称
name: PR-QA-workflow
# 触发方式
on:
workflow_dispatch:
pull_request:
# 全局变量
env:
RELEASE_HOST: 192.168.1.45
RUSTFLAGS: "-C instrument-coverage --cfg tokio_unstable"
ARTIFACT_BASE_DIR: /data/artifacts
COVERAGE_BASE_DIR: /data/coverage
DIR_PATH: ${{ github.event.number }}
RUN_UNIT_TEST_FILE_FLAG: /etc/taos/taosx_run_unit_test
# 具体的job
jobs:
build_rust:
...
build_go:
...
下文将按照 workflow 的顺序,对每个环节进行逐一介绍。
用于触发 workflow 的配置如下:
on:
workflow_dispatch:
pull_request:
说明:
关于触发事件的完整描述可参考:Events that trigger workflows
在 workflow 中,我们设置了一些全局的环境变量,以供 workflow 中的 job 使用,具体如下所示:
env:
RELEASE_HOST: 192.168.1.45
RUSTFLAGS: "-C instrument-coverage --cfg tokio_unstable" # rust 用于获取覆盖率的编译用环境变量
ARTIFACT_BASE_DIR: /data/artifacts # 编译之后编译中间文件及可执行文件存放的根目录
COVERAGE_BASE_DIR: /data/coverage # 生成报告后报告存放的根目录
DIR_PATH_3_0: "3.0" # 下一级使用 3.0 或 PR Number
DIR_PATH: ${{ github.event.number }} # 下一级使用 3.0 或 PR Number
RUN_UNIT_TEST_FILE_FLAG: /etc/taos/taosx_run_unit_test
在编译过程中,taosx、taosx-agent、opc 以及 mqtt 各个组件需要设置特定的环境变量,以便输出插桩程序。由于这些组件在编译过程中没有相互依赖,因此设置了三个可以并行执行的任务,分别负责各自组件的编译工作。
build_rust:
runs-on: [self-hosted, linux, x64, qa, rust]
build_go:
runs-on: [self-hosted, linux, x64, qa, go]
build_java:
runs-on: [self-hosted, linux, x64, qa, java]
三个编译任务—Rust编译、Go编译、Java编译—之间不存在依赖关系,且各自在不同的机器上运行。因此,这三个任务可以同时并行执行。
在 taosx 及 taox-agent 编译和单元测试执行完成之后,需要将编译过程中产生的中间文件及可执行程序拷贝至特定目录,用以留存和区分。
- name: Copy files
run: |
ls $ARTIFACT_BASE_DIR/$DIR_PATH/bin || mkdir -p $ARTIFACT_BASE_DIR/$DIR_PATH/bin
cp ./target/debug/taosx-build/debug/taosx ./target/debug/taosx-agent-build/debug/taosx-agent $ARTIFACT_BASE_DIR/$DIR_PATH/bin/
cp -r ./target/debug/taosx-profraw/ $ARTIFACT_BASE_DIR/$DIR_PATH/
cp -r ./target/debug/taosx-agent-profraw/ $ARTIFACT_BASE_DIR/$DIR_PATH/
cd ..
tar --exclude=./taosx/target/* -zcvf taosx.tar.gz ./taosx
cp taosx.tar.gz $ARTIFACT_BASE_DIR/$DIR_PATH/
rm taosx.tar.gz
tar -xzf $ARTIFACT_BASE_DIR/$DIR_PATH/taosx.tar.gz -C $ARTIFACT_BASE_DIR/$DIR_PATH/
rm $ARTIFACT_BASE_DIR/$DIR_PATH/taosx.tar.gz
目前运行了 taosx、opc、mqtt 三个组件的单元测试,单元测试的运行与编译处于同一阶段。
前期由于单元测试可能存在不稳定,或者单元测试耗时较长等问题的存在,添加了依据 /etc/taos/taosx_run_unit_test 该文件是否存在执行单元测试的逻辑。若该文件存在,则执行。
taosX 编译及运行单元测试的配置如下:
- name: Build taosx
run: |
# build taosx and taosx-agent
# export RUSTFLAGS="-C instrument-coverage --cfg tokio_unstable"
rm -rf ./target/debug/taosx-profraw/
export LLVM_PROFILE_FILE="./target/debug/taosx-profraw/taosx-%p-%m.profraw"
cargo build --target-dir ./target/debug/taosx-build/
- name: taosx unit test
continue-on-error: true
run: |
if [ -f "$RUN_UNIT_TEST_FILE_FLAG" ]; then
echo "run taosx unit test"
cargo llvm-cov nextest --workspace
cargo llvm-cov report --lcov --output-path $ARTIFACT_BASE_DIR/$DIR_PATH/taosx-unit-test-lcov.info
fi
taosX 运行完单元测试之后,会输出单元测试覆盖率报告到 $ARTIFACT_BASE_DIR/$DIR_PATH/,用于之后与端到端测试的覆盖率结果进行合并。
MQTT 编译机运行单元测试的配置如下:
- name: Build mqtt
run: |
cd plugins/mqtt
go build -cover -o taosx-mqtt
- name: Run Mqtt Unit Test
continue-on-error: true
run: |
cd plugins/mqtt
if [ -f "$RUN_UNIT_TEST_FILE_FLAG" ]; then
echo "run mqtt unit test"
go test -coverpkg=./... -coverprofile=mqtt_unit_report.txt -timeout=5s ./...
fi
- name: Copy MQTT Files
run: |
cd plugins/mqtt
if [ -f "mqtt_unit_report.txt" ]; then
ssh root@$RELEASE_HOST "ls $ARTIFACT_BASE_DIR/$DIR_PATH/go_cover/mqtt_cover_data/ || mkdir -p $ARTIFACT_BASE_DIR/$DIR_PATH/go_cover/mqtt_cover_data/"
scp mqtt_unit_report.txt root@$RELEASE_HOST:$ARTIFACT_BASE_DIR/$DIR_PATH/go_cover/mqtt_cover_data/
rm mqtt_unit_report.txt
fi
ssh root@$RELEASE_HOST "ls $ARTIFACT_BASE_DIR/$DIR_PATH/bin/mqtt || mkdir -p $ARTIFACT_BASE_DIR/$DIR_PATH/bin/mqtt/"
scp taosx-mqtt root@$RELEASE_HOST:$ARTIFACT_BASE_DIR/$DIR_PATH/bin/mqtt/
同样,MQTT 的单元测试报告在单元测试完成之后会输出,并拷贝到 $ARTIFACT_BASE_DIR/$DIR_PATH/go_cover/mqtt_cover_data/,用于之后与端到端测试覆盖率报告合并。
在部署过程中,需要进行两次部署操作。首先是为 MQTT 用例的执行进行部署。在这一阶段,由于 taosx 或 taosx-agent 需要以子进程的形式启动 taosx-mqtt,因此在运行 MQTT 用例时,必须配置相应的环境变量。如下:
steps:
- name: Deploy taosx
run: |
pwd
ls ${{ github.workspace }}
# run taosx
ssh root@$RELEASE_HOST "ls $ARTIFACT_BASE_DIR/$DIR_PATH_3_0/go_cover/mqtt_cover_data/ || mkdir -p $ARTIFACT_BASE_DIR/$DIR_PATH_3_0/go_cover/mqtt_cover_data/"
ssh root@$RELEASE_HOST "nohup sh -c 'DATABASE_URL=/var/lib/taos/taosx/taosx.db LLVM_PROFILE_FILE=$ARTIFACT_BASE_DIR/$DIR_PATH_3_0/taosx-profraw/taosx-%p-%m.profraw GOCOVERDIR=$ARTIFACT_BASE_DIR/$DIR_PATH_3_0/go_cover/mqtt_cover_data/ PLUGINS_HOME=$ARTIFACT_BASE_DIR/$DIR_PATH_3_0/bin/ ENABLE_COVERAGE=true nohup $ARTIFACT_BASE_DIR/$DIR_PATH_3_0/bin/taosx serve > $ARTIFACT_BASE_DIR/$DIR_PATH_3_0/bin/taosx.log 2>&1 &'"
- name: Deploy taosx-agent
run: |
pwd
ls ${{ github.workspace }}
# run taosx-agent
ssh root@$RELEASE_HOST "nohup sh -c 'GOCOVERDIR=$ARTIFACT_BASE_DIR/$DIR_PATH_3_0/go_cover/mqtt_cover_data/ PLUGINS_HOME=$ARTIFACT_BASE_DIR/$DIR_PATH_3_0/bin/ ENABLE_COVERAGE=true LLVM_PROFILE_FILE=$ARTIFACT_BASE_DIR/$DIR_PATH_3_0/taosx-agent-profraw/taosx-agent-%p-%m.profraw nohup $ARTIFACT_BASE_DIR/$DIR_PATH_3_0/bin/taosx-agent > $ARTIFACT_BASE_DIR/$DIR_PATH_3_0/bin/taosx-agent.log 2>&1 &'"
第二次部署与第一次部署大体相同,区别是 GOCOVERDIR 的赋值会修改为 OPC 准备的目录。
GOCOVERDIR=$ARTIFACT_BASE_DIR/$DIR_PATH/go_cover/opc_cover_data
在部署完成之后,就可以执行端到端的测试。由于第一次部署设置的是 MQTT 的覆盖率文件的路径,所以第一次运行的测试也是 MQTT 的用例。
端到端测试的代码已经存放至 /data/github/TestNG_taosX,每次执行之前只需要使用 git pull 更新代码即可。
在执行完端到端测试之后应该停止 taosx 及 taosx-agent 用于获取 taosx 及 agent 的覆盖率信息。此时需要通过 kill -2 关闭,这样能够发送 SIGINT 信号给指定的进程,使得进程能够优雅退出,不能通过 kill -9 否则无法成功获取覆盖率信息。
MQTT 的端到端测试用例的执行配置如下:
test_mqtt:
runs-on: [self-hosted, linux, x64, qa, rust]
needs: deploy_for_mqtt
steps:
- name: Update Repo
run: |
# 这里最好是 TestNG_taosx 已经被拉取到一个固定的位置
# 重新登录执行 git pull,因为当前的 git 仓库对应的是 taosx
ssh root@$RELEASE_HOST "cd /data/github/TestNG_taosX && git pull"
- name: Run Test
run: |
ls $COVERAGE_BASE_DIR/$DIR_PATH/allure_profile || mkdir -p $COVERAGE_BASE_DIR/$DIR_PATH/allure_profile
cd /data/github/TestNG_taosX && source ./setenv.sh && poetry install && cd tests && poetry run pytest --alluredir=$COVERAGE_BASE_DIR/$DIR_PATH/allure_profile --timeout=300 -m sanity mqtt_test.py
- name: Stop taosx-agent & taosx
run: |
pids=$(pgrep taosx-agent); if [ -n "$pids" ]; then for pid in $pids; do "run kill -9 taosx-agent"; kill -2 "$pid"; done; fi
pids=$(pgrep taosx); if [ -n "$pids" ]; then for pid in $pids; do "run kill -9 taosx"; kill -2 "$pid"; done; fi
- name: Force stop taosx-agent & taosx
if: always()
run: |
pids=$(pgrep taosx-agent); if [ -n "$pids" ]; then for pid in $pids; do "run kill -9 taosx-agent"; kill -9 "$pid"; done; fi
pids=$(pgrep taosx); if [ -n "$pids" ]; then for pid in $pids; do "run kill -9 taosx"; kill -9 "$pid"; done; fi
执行除 MQTT 之外的用例的 pytest 命令:
cd /data/github/TestNG_taosX && source ./setenv.sh && poetry install && cd tests \
&& poetry run pytest --alluredir=$COVERAGE_BASE_DIR/$DIR_PATH_3_0/allure_profile \
--timeout=300 -m sanity -k "not mqtt_test.py"
说明:
poetry install 安装测试依赖pytest 命令来运行自动化测试,通过-m 参数指定执行带有 sanity 标签的用例--alluredir 参数指定报告的目录--timeout 指定每个用例的超时时间,避免出现用例长时间挂起的情况Job 的日志截图:

taosX 的覆盖率报告截图:

OpenTSDB 的覆盖率报告截图:

OPC 的覆盖率报告截图:

这里的测试报告则是 pytest 运行完成之后,端到端测试的测试报告。测试报告生成的逻辑如下:
- name: allure report
run: |
allure generate $COVERAGE_BASE_DIR/$DIR_PATH_3_0/allure_profile -o $COVERAGE_BASE_DIR/$DIR_PATH_3_0/allure_report/ --clean
# 这里会直接输出报告的地址
echo "see report at $REPORT_BASE_URL/$DIR_PATH_3_0/allure_report/"
Job 的日志如下:

其中的测试报告链接可以直接点击,测试报告如下图所示:

1. 开发者通过提交 PR 触发 CI 运行。

2. 通过点击执行的 Action 可以查看执行的结果。

开发者在查看该 Action 的时候可以在底部看到流程中哪个 Job 的运行是出错了的。
3. 可以通过点击下方的出错的任务查看任务出错的原因。

4. 最后可以通过点击 generate_report 中查看报告的具体结果。

综上所述,我们已经实现了一项自动化功能:每当开发人员在 taosX 仓库提交 Pull Request(PR)时,即自动触发 CI 流程。通过这一流程,开发和测试人员能够利用生成的测试报告和覆盖率报告来评估 PR 中的代码变更。如果你也对 taosX 这一工具感兴趣,欢迎添加小T微信(tdengine)与 loveini 的米兰app官方正版下载专家进行一对一沟通。我们将提供专业的指导,帮助你更深入地了解和应用 taosX。
]]>基于 DataX,我们完成了 loveini 的适配,对于 loveini 3.0 版本,实现了 loveini30Reader 和 loveini30Writer 两个插件。
loveini30Reader 提供的功能:
loveini30Writer 支持的功能:
loveini30Reader:使用 JNI 方式从 loveini 拉取数据。
loveini30Writer:使用 JNI 方式写数据到 loveini。对 OpenTSDB 等使用 schemaless 写入,对于 MySQL 等关系型数据库,使用批量 stmt 写入。
(1)需要安装 loveini 客户端
(2)需要安装 JDK 1.8 环境(运行 DataX)
(3)需要安装 Python 环境(运行 DataX)
(4)需要 maven 编译环境(如果不编译 DataX 则可以不安装 maven)
下载源码
git clone https://github.com/taosdata/DataX.git
编译打包
cd DataX
mvn -U clean package assembly:assembly -Dmaven.test.skip=true
安装
cp target/datax.tar.gz your_install_dir
cd your_install_dir
tar -zxvf dataX.tar.gz
以一个从 OpenTSDB 到 loveini 3.0 版本的数据迁移任务为例,配置文件 opentsdb2tdengine.json 如下:
{
"job":{
"content":[{
"reader": {
"name": "opentsdbreader",
"parameter": {
"endpoint": "http://192.168.1.180:4242",
"column": ["weather_temperature"],
"beginDateTime": "2021-01-01 00:00:00",
"endDateTime": "2021-01-01 01:00:00"
}
},
"writer": {
"name": "tdengine30writer",
"parameter": {
"username": "root",
"password": "taosdata",
"connection": [
{
"table": [
"matric1"
],
"jdbcUrl": "jdbc:TAOS://192.168.1.101:6030/test?timestampFormat=TIMESTAMP"
}
],
"batchSize": 1000,
"ignoreTagsUnmatched": true
}
}
}],
"setting": {
"speed": {
"channel": 1
}
}
}
}
配置说明:
以一个从 MySQL 到 loveini 3.0 版本的数据迁移任务为例,配置文件 mysql2tdengine.json 如下:
{
"job": {
"content": [{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "root",
"password": "root",
"column": ["id","name"],
"splitPk": "id",
"connection": [{
"table": ["test"],
"jdbcUrl": ["jdbc:mysql://192.168.1.101:3306/db"]
}]
}
},
"writer": {
"name": "tdengine30writer",
"parameter": {
"host": "192.168.1.105",
"port": 6030,
"dbname": "test",
"user": "root",
"password": "taosdata",
"batchSize": 1000
}
}
}],
"setting": {
"speed": {
"channel": 1
}
}
}
}
配置说明:
以一个从 loveini 到 loveini 的数据迁移为例,配置文件 tdengine2tdengine.json 如下:
{
"job": {
"content": [{
"reader": {
"name": "tdengine30reader",
"parameter": {
"host": "192.168.1.82",
"port": 6030,
"db": "test",
"user": "root",
"password": "taosdata",
"sql": "select * from weather",
"beginDateTime": "2021-01-01 00:00:00",
"endDateTime": "2021-01-02 00:00:00",
"splitInterval": "1h"
}
},
"writer": {
"name": "tdengine30writer",
"parameter": {
"host": "192.168.1.105",‘
"port": 6030,
"dbname": "test",
"user": "root",
"password": "taosdata",
"batchSize": 1000
}
}
}],
"setting": {
"speed": {
"channel": 1
}
}
}
}
配置说明:
将上面写好的配置文件保存在 datax/job 目录下,执行下面的命令,启动数据迁移任务:
python bin/datax.py job/opentsdb2tdengine.json
(1)目前,DataX 自带的 opentsdbreader 仅支持 OpenTSDB-2.3.X 版本。详细参考:opentsdbreader#约束限制
(2)数据迁移工具依赖 loveini 客户端中的 libtaos.so/taos.dll/libtaos.dylib,需要与服务端对应版本的 loveini-client。
(1)如何估算一个数据迁移任务所需要的资源
DataX 的每个 reader 按照自己的 task 切分策略进行任务划分,具体请参考 DataX 的任务调度规则。在估算资源是,需要按照数据迁移的数据量,任务切分规则和网络带宽限制等综合考虑,最好以实际数据迁移测试结果为准。
(2)loveini30Writer 的 batchSize 设置多大效率最高?
batchSize 是控制批量写入的参数,在获取 batchSize 行纪录后,loveiniWriter 会向 loveini 发送一次写入请求,这减少了与 loveini 交互次数,从而提高了性能。从测试结果来看,batchSize 在 500-1000 范围内效率最高。
(3)job 的配置中 channel 数为多少合适?
job 中的 channel 数为流量控制的参数,每个 channel 都需要开辟一块内存,用来缓存数据。如果 channel 设置过大,会引起 OOM,所以 channel 数并不是越大越好。增加 channel 数后,需要提高 JVM 内存大小。从测试结果来看,channel 在 1~6 的范围内都是合适,能够保证 DataX 的流量最大化即可。
(4)java.sql.SQLException: loveini ERROR (8000060b): Invalid client value
配置文件中 column 中没有配置 tbname,此时会触发行协议数据写入(行协议写入只会自动创建子表名,但需要提前创建好超级表),行协议写入的情况下不支持 TAG 数据类型为非 NCHAR,所以此种情况有两种米兰app官方正版下载:1.将 TAG 全部修改为 NCHAR 类型;2.在 Column 中配置好表名称这样不会触发行协议写入。
(5)java.sql.SQLException: loveini ERROR (8000060b): Timestamp data out of range
配置文件中 column 中没有配置 tbname,此时会触发行协议数据写入,且 TAG 全部为 NCHAR 类型,此时需要保证时间戳的一列名称为 _ts,而不能是其他名称(行协议写入下,默认将最后的时间戳写入到 _ts 一列,且不能随意命名)。若想避免请使用 tbname 指定表名以避免触发行协议写入。
]]>