用于捕获统计信息的 InfluxDB C 客户端库
作者:社区 / 产品, 用例, 开发者
2021年2月23日
导航至
这是对 InfluxAce、IBM 技术人员、AIX 和 Linux 平台 nmon 开发者 Nigel Griffiths 撰写的文章的转载,该文章最初发布在 IBM 网站上。
目前,没有官方的 InfluxDB C 语言客户端库。幸运的是,我正想为 AIX 和 Linux 操作系统性能统计信息的捕获做这件事。这个数据捕获工具名为 “njmon”,并在 Sourceforge 上开源。在弄清楚方法并为自己开发了一个包含 12 个函数的小型库,以简化数据保存后,我想我会分享它。我希望它对其他人有所帮助。
InfluxDB 关于 Line Protocol 格式统计信息的文档很好,但 HTTP 要求有些模糊,您必须通过多次实验来发现细节。这个库也包括了网络套接字处理层,这在第一次可能很棘手。
这个 InfluxDB C 客户端库不包含从 InfluxDB 提取数据的函数(JSON 数据处理在 C 语言中非常困难),也不包含数据库管理功能,如创建 InfluxDB 数据库(我使用 “influx” 命令)。
环境
假设
- 您已安装 C 语言编译器。
- 您可以编写一个小的 C 语言程序。
- 您正在运行 InfluxDB。
此代码已在以下操作系统和硬件的当前版本上测试过
- AIX 7
- Linux RHEL、SUSE 和 Ubuntu (ppc64le (POWER8/9))
- Linux RHEL、SUSE 和 Ubuntu (AMD64 (x86_64))
由于 C 代码很简单,它可能在其他操作系统上也能工作。该代码使用了常规的套接字库函数。
步骤
一个小型示例,展示了使用 C 语言捕获统计信息并将它们添加到 InfluxDB 所需的代码(简称为 “ic” 函数)
#include "ic.h"
main()
{
ic_influx_database("myinfluxdbserver", 8086, "ic"); /* the details of the connection to InfluxDB */
ic_tags("host=myserver.acme.com"); /* tags to find specific data amongst the data */
for(;;) {
ic_measure("mystats"); /* name of a group of data */
ic_long("busy", 42); /* example of saving an integer */
ic_double("pi", 3.142); /* example of saving a floating point number */
ic_string("state", "Running"); /* example of saving some text */
ic_measureend(); /* end the group */
ic_push(); /* after many measures it is sent to InfluxDB over the network in one go*/
sleep(60); /* pause 60 seconds and then repeat */
}
在这个微小的示例中,统计信息(42, 3.141, “Running”)是硬编码的。这不是一个好主意,而且相当无意义。希望这段代码能说明如何使用库函数处理各种 C 数据类型。通常,C 代码会包含 C 函数调用,以从操作系统、应用程序库或通过执行命令来获取实时数据。本文的其余部分将介绍这些 “ic” 函数以及更多细节。
在本文末尾,您可以从 GitHub 下载代码和使用示例。
C 函数介绍
提供的函数如下(按正常使用顺序排列)
void ic_influxdb(char * hostname, long port, char * database)
- 在 C 程序的开头仅使用此函数一次。
- 此函数提供 InfluxDB 服务器的连接详细信息。
- 例如,ic_influxdb("admin.domain.company.com", 8086, "ic")
- 主机名很明显。InfluxDB 的默认端口是 8086。可以在 InfluxDB 配置文件中更改它,但需要重启 InfluxDB 才能激活。
- 数据库需要在运行程序之前创建。假设数据库名称为 “ic”,在 InfluxDB 服务器上
$ influx > create database ic > exit $
- 您可以使用 InfluxDB 服务器的主机名或 IP 地址。
- 此函数仅记录详细信息。与 InfluxDB 的连接仅在数据被推送出去 (ic_push()) 时建立,并在之后立即断开连接。
void ic_influx_userpw(char *user, char *password)
- 在 C 程序的开头仅使用此函数一次。
- 例如,ic_influx_userpw("Nigel", "SECRET");
- 如果您的 InfluxDB 需要用户名和密码才能访问,请使用此函数提供详细信息,以便稍后连接到 InfluxDB。
- 如果您的 InfluxDB 服务器没有强制性的用户名和密码(例如,默认安装配置),则您不需要调用此函数。如果不需要,InfluxDB 会默默地忽略任何用户名和密码。
- 尚不支持 SSL 证书。
void ic_tags(char *tag_string)
- 在 C 程序的开头仅使用此函数一次。
- 标签用于缩小数据库数据范围,以针对特定资源。
- 计算机性能统计信息示例:主标签通常是收集统计信息的服务器的主机名。图形工具可以使用标签值将数据限制为子集。例如,通过主机名选择关于特定服务器的 CPU 统计信息。
- 例如,ic_tags("host=server42")
- 您可以拥有多个标签。标签以逗号分隔,且不包含空格:
ic_tags("host=server42,serialno=75629475")
- 标签中完全不能有空格或制表符。最好避免使用它们。
- 有一种特殊情况,如果您有多个相同类型的资源,例如,多个磁盘,那么您可以使用
- ic_tags("host=server42,serialno=75629475,disk=sda")
- ic_tags("host=server42,serialno=75629475,disk=sdb")
- 等等。
- 这些额外的标签允许在一个图表中绘制所有磁盘,Grafana 可以计算出每台服务器有多少个磁盘,并调整图表以匹配 —— 更多细节将在本文后面介绍。
InfluxDB Line Protocol — 简要介绍
InfluxDB Line Protocol 需要以下几个部分
measurement-name, tags, 一个空格, key=value 对, 一个空格 和 时间戳
Measurement-name 最好是有意义的,例如:cpu、memory、disk、transactions、temperature 等等。这些名称列在图形工具中,因此清晰明了的名称可以帮助选择正确的统计信息。
标签标识数据的来源和用于选择数据的特征。例如,服务器主机名 (host=vm99)、数据来源的仪器 (probe=a12)、应用程序名称 (app=DB2)。
空格 表示标签的结束和键值对的开始。key=value 是实际数据,例如 “cpubusy”=56 因此
- 格式:
measurement,tags key=value timestamp
CPUutilisation,host=vm123 cpu_user=60,cpu_sys=15,cpu_wait=15,cpu_idle=10 timestamp
- ic 库让 InfluxDB 添加时间戳(自 1970 年 1 月 1 日起的秒数),从而确保跨收集工具的时间一致性。
捕获您的统计信息
- 假设您知道如何在 C 程序中捕获您的实际数据。
- 数据通常来自操作系统或随您安装的额外工具和应用程序提供的库的 C 库函数调用。
- 本文开头的微小示例介绍了基础知识。以下是详细信息
- ic_measure()
- 然后,根据数据格式,使用多个函数来保存数据
- ic_long() 用于 C 语言的 long 类型(大整数(技术上是 64 位 long long))
- ic_double() 用于 C 语言的 double 类型(大浮点数)
- ic_string() 用于 C 语言的字符串(以零字符结尾的字符数组)
- 最后使用 ic_measureend()
void ic_measure(char *measurement_name)
- 此函数命名 measurement(一组统计信息),并以匹配的 ic_measureend() 调用结束。节名称保存在输出缓冲区中。
- 您可以拥有多个 ic_measure() 和 ic_measureend() 对,以保存不同的资源。例如,计算机统计信息:CPU、内存、磁盘、网络等等。
- 例如,ic_measure("CPUutilisation");
void ic_measureend()
- 此函数用于标记当前 measurement 的结束。
void ic_long(char *name, long long value)
- 此函数将名称和整数保存到输出缓冲区。
- 在一个 measure 中可以有多个 ic_long() 函数调用。
- 例如
ic_long("cpu_user", 60);
ic_long("cpu_sys", 15);
ic_long("cpu_wait", 15);
ic_long("cpu_idle", 10);
void ic_double(char *name, double value)
- 此函数将名称和双精度浮点数保存到输出缓冲区。
- 在一个 measurement 中可以有多个 ic_double() 函数调用。
- 例如
ic_double("cpu_user", 60.3);
ic_double("cpu_sys", 15.1);
ic_double("cpu_wait", 15.2);
ic_double("cpu_idle", 9.4);
关于在 InfluxDB 中使用长整数和双精度浮点数的注意事项
- 在 InfluxDB 中,使用整数而不是双精度浮点数几乎没有空间上的优势。
- 此外,InfluxDB 很智能,不会在精确比较中混淆,例如将双精度浮点数与零进行比较。
- 我建议您在大多数情况下使用双精度浮点数值。
- 一旦数据被添加到数据库,就无法尝试在 InfluxDB 中将数据类型从整数更改为双精度浮点数。因此,请在第一次就正确设置数据类型。如果您尝试添加 “key = 浮点数”,但该 key 已经被写入 InfluxDB 为整数,那么整个 measurement 将被拒绝。
- 当我以为数据显然是整数,例如运行队列号时,我遇到了这样的问题。然后,后来意识到运行队列增量需要除以秒数。因此,它变成了一个浮点数。可行的选择是删除数据库中的所有当前数据,或者将 “runqueue” 重命名为略有不同的名称,例如 “runq”。
void ic_string(char *name, char *value)
- 此函数将名称和字符串保存到输出缓冲区。
- 在一个 measure 中可以有多个 ic_string() 函数调用。
- 例如
ic_string("architecture", "ppc64le");
ic_stringe("os_version", "7.2.5.2");
ic_string("os_name", "AIX");
ic_string("vendor", "IBM");
ic_string("status", "Running")
ic_string("location", "London, UK");
不要像这样使用它,因为字符串不能被绘制成图表
is_string("processes", "42") ;
is_string("processes", "42.12345") ;
is_string("meaning", "forty two");
void ic_push()
- 一旦所有数据都被保存,就需要将其发送到 InfluxDB。ic_push() 函数执行此操作。
- ic_push() 函数打开一个网络套接字,InfluxDB 服务器通过该套接字发送所有已保存的数据,然后关闭套接字。一次性推送所有数据是一种有效的方法,可以一次发送可能数千个统计信息。
- 之后,套接字连接被关闭。
- 由于与 InfluxDB 的连接只是临时的,这意味着
- 如果 InfluxDB 因软件更新到较新版本而重启,或者服务器重启,则不会有问题。
- 它还可以处理短暂的网络中断。
- 如果无法创建套接字,数据将被丢弃,但下一次 ic_push() 将正常工作。
- 通常,您的程序结构如下所示
Initialise functions
while(1) {
ic_measure()
add data to this measure
ic_measureend()
ic_measure()
add data to this measure
ic_measureend()
ic_measure()
add data to this measure
ic_measureend()
...
ic_push()
sleep(for some seconds)
}
始终将 sleep() 放在 ic_push() 之后,以便数据收集和 ic_push() 之间的时间尽可能短。
如果您有多个相同类型的单元会发生什么?
来自计算机统计信息领域的一个例子,您的笔记本电脑只有一个磁盘,但较大的服务器可能有几十甚至几百个磁盘,并且每台服务器可能具有不同数量的磁盘。这使得图形化变得棘手,因为图表上可能有一到数百条线。CPU 核心、网络、文件系统、网络缓冲区、光纤通道适配器等等也是如此。
为了处理这个问题,我们有所谓的 measurement 内的子 measurement 统计信息。
void ic_sub(char *sub_name)
- 此函数在 ic_measurement() 和 ic_measurementend() 函数中使用。
- sub_name 被添加到此 measurement 的标签中,直到遇到匹配的 ic_subend()。
- 在 ic_sub() 和 ic_subend() 之间,使用常规的 ic_long()、ic_double() 和 ic_string() 来保存数据。
void ic_subend()
- 结束子 measurement,即与之前的 ic_sub() 配对。
- 由之前的 ic_sub() 添加的标签被移除。
一个简单的例子 — 为了帮助说明这一点。缩进不是必需的(在 C 语言中被忽略),但在这里用于澄清函数对。
ic_measurement("disks");
ic_sub("hdisk0");
is_double("reads", 45.6);
is_double("writes",105.83);
ic_subend();
ic_sub("hdisk1");
is_double("reads", 2084.91);
is_double("writes",4.1);
ic_subend();
ic_meassurementend();
最后一个函数是打开调试输出
void ic_debug(int level)
- 可以随时调用此函数,但如果您遇到问题,最好在开始时调用此函数。
- 只有三个值有效
- Level = 0 表示调试输出已关闭。库仅针对警告(可恢复的错误)和错误(其中 ic 将导致程序退出)输出。
- Level = 1 表示跟踪输出,因为库中的每个数字都被调用,因此您可以轻松查看您的程序如何与库交互。此外,对于 ic_push(),还会输出到 InfluxDB 的基本信息标头和响应。
- Level = 2 表示与 level = 1 相同,此外还会输出 InfluxDB Line Protocol 的详细信息 — 如果您要向 InfluxDB 写入大量数据,这可能会很大。
编译
有经验的 C 程序员会发现只有 300 行代码,主要在 ic.c 中,以及在 example.c 中使用它的示例。代码在 GitHub 上:github.com/nigelargriffiths/InfluxDB-C-client。文件包括
- ic.h - 用于访问 ic.c 中函数的头文件
- ic.c - 实际的 ic 代码。通过将其与您的应用程序代码或 example.c 代码一起编译来使用它。此外,也可以通过创建库来使用它。
- example.c - 简单的示例代码,帮助您开始正确使用该函数。包括启动函数,然后是简单的 measurement 和带有子数据的 measurement。
- ic_all_in_one.c - 这是 ic.c 和 example.c 代码在一个文件中的版本。如果您想快速编译然后尝试代码,这将非常有用。
- InfluxDB_C_client_example_Grafana_Dashboard.json - 可用的 Grafana 仪表板,允许更改主机名
选项包括
使用 ic.c 文件作为数据收集器程序的基础。将您的代码替换为示例 main() 函数中的代码。
- 选项 1:使用以下命令编译
cc ic_all_in_one.c -g -O3 -o ic_test
如果您的编译器报错关于 “isnan”,则将数学库添加到编译器命令中:-lm
- 选项 2:创建一个可以添加到您的应用程序的库。在您的应用程序中包含 ic.h 头文件,然后调用 ic 库函数
# Compile as a library archive
cc ic.c -g -O3 -c
# Check you have an archive
ls -l ic.a
# Compile other code and include the library
cc -g -O3 mycode.c ic.o -o myapp
运行示例代码
在 ic_all_in_one.c 文件的末尾,main() 函数中有示例数据捕获代码。可以编译它以帮助您入门。您将必须修改 mail() 函数以及到您的 InfluxDB 和 InfluxDB 数据库的连接详细信息。
示例代码如下所示
ic_influx_database("silver2", 8086, "ic");
ic_influx_userpw("nigel", "secret");
ic_debug(2);
按如下方式更改这些代码行
- 将 “silver2” 更改为您的 InfluxDB 服务器的主机名。您也可以使用 IP 地址。
- 8086 是 InfluxDB 的默认端口号 - 如果使用非标准的 InfluxDB 端口,请更改此端口。
- “ic” 是 InfluxDB 服务器中的数据库名称(也称为 bucket)- 我建议使用 “ic” 进行快速测试。通常,我们会为数据库选择一个有意义的名称。确保您已创建名为 “ic” 或您决定的任何名称的 InfluxDB 数据库。
- 如果您已为访问 InfluxDB 设置了用户名和密码,则将使用它们
- 您可能需要使用 ic_debug(level) 设置调试级别。其中 0=“无输出”,1=“输出您对 ic 函数的调用”,2=“还输出发送到 InfluxDB 的数据的详细信息。”
使用以下命令编译示例代码
cc ic_all_in_one.c -g -O3 -o ic_test
您现在有了一个名为 “ic_test” 的可执行二进制文件。
debug = 2 时的示例输出(所有可能的调试消息)。
[nag@silver2 ic]$ ./ic_test
ic_influx_by_hostname(host=silver2,port=8086,database=ic))
ic_influx_by_hostname hostname=silver2 converted to ip address 9.137.62.12))
ic_influx_userpw(username=nigel,password=secret))
ic_tags(host=silver2.aixncc.uk.ibm.com)
ic_measure("cpu") count=35
ic_long("user",30) count=44
ic_double("system",8.3) count=57
ic_string("status","low") count=70
ic_measureend()
ic_measure("disks") count=110
ic_sub("sda1") count=125
ic_long("reads",26) count=135
ic_double("writes",73.3) count=149
ic_subend()
ic_sub("sda2") count=204
ic_long("reads",51) count=214
ic_double("writes",101.5) count=229
ic_subend()
ic_measureend()
ic_push() size=232
socket: trying to connect to "9.137.62.12":8086
buffer size=88
buffer=<POST /write?db=ic&u=nigel&p=secret HTTP/1.1
Host: silver2:8086
Content-Length: 232
>
output size=232 output=
<cpu,host=silver2.aixncc.uk.ibm.com user=30i,system=8.284,status="low"
disks,host=silver2.aixncc.uk.ibm.com,disk_name=sda1 reads=26i,writes=73.266
disks,host=silver2.aixncc.uk.ibm.com,disk_name=sda2 reads=51i,writes=101.544
>
written=232 bytes sent=0 total=232
received bytes=249 data=<HTTP/1.1 204 No Content
Content-Type: application/json
Request-Id: 9f6edc98-584f-11eb-be71-3a71321b9604
X-Influxdb-Build: OSS
X-Influxdb-Version: 1.7.10
X-Request-Id: 9f6edc98-584f-11eb-be71-3a71321b9604
Date: Sat, 16 Jan 2021 23:07:36 GMT
>
http-code=204 text=No Content [204=Success]
ic_push complete
注释
- “26i” 和 “51i” 是整数值的表示法。
- 以 ic_ 开头的行告诉您如何调用 ic 包。
- 在 ic_push() 之后,它记录了与 InfluxDB 的交互。
- 发送的数据通过套接字以 HTTP 格式输出,包括 POST 详细信息。
- Influx Line Protocol 数据。
- ic_push() 获取从 InfluxDB 返回的结果。这是一个简单的确认消息。
- 204 消息和文本 “No Content” 表示成功。InfluxDB 回复 “no comment”;也就是说,“没有错误”,数据已保存。如果数据有问题,则 InfluxDB 会返回其他错误代码,以及描述问题和需要更改的内容的文本。
我们能在 InfluxDB 中找到数据吗?
一旦您使用 “ic” 库和程序将统计信息保存到 InfluxDB,最好在尝试绘制图形之前检查数据是否已正确保存。这可以使用名为 “influx” 的 InfluxDB 命令行界面来完成,只需要几个简单的命令。这里我们选择 “ic” 的数据库(use ic)并列出已保存的 measurement(show measurements)。此示例输出来自运行 ic.c 文件中的示例代码,因此 cpu 和 disk 统计信息是伪造的数据。
[nag@silver2 ic]$ influx
Connected to http://localhost:8086 version 1.7.10
InfluxDB shell version: 1.8.3
> use ic
Using database ic
> show measurements
name: measurements
name
----
cpu
disks
> select * from cpu
name: cpu
time host status system user
---- ---- ------ ------ ----
1610838551375866432 silver2.aixncc.uk.ibm.com low 8.284 30
1610838552391246233 silver2.aixncc.uk.ibm.com high 8.284 39
1610838553392913108 silver2.aixncc.uk.ibm.com high 6.284 60
1610838554394548129 silver2.aixncc.uk.ibm.com high 9.284 24
> select * from disks
name: disks
time disk_name host reads writes
---- --------- ---- ----- ------
1610838551375866432 sda0 silver2.aixncc.uk.ibm.com 1 1
1610838551375866432 sda1 silver2.aixncc.uk.ibm.com 26 51.272
1610838551375866432 sda2 silver2.aixncc.uk.ibm.com 25 57.556
1610838552391246233 sda0 silver2.aixncc.uk.ibm.com 1 1
1610838552391246233 sda1 silver2.aixncc.uk.ibm.com 24 51.272
1610838552391246233 sda2 silver2.aixncc.uk.ibm.com 1 38.704
1610838553392913108 sda0 silver2.aixncc.uk.ibm.com 1 1
1610838553392913108 sda1 silver2.aixncc.uk.ibm.com 10 7.284
1610838553392913108 sda2 silver2.aixncc.uk.ibm.com 41 13.568
1610838554394548129 sda0 silver2.aixncc.uk.ibm.com 1 1
1610838554394548129 sda1 silver2.aixncc.uk.ibm.com 13 57.556
1610838554394548129 sda2 silver2.aixncc.uk.ibm.com 59 170.668
> exit
用于可视化 InfluxDB 数据的 Grafana 示例图表
Grafana 是绘制 InfluxDB 数据的常用选择,这里有一些伪造数据的简单折线图。
在 Grafana 仪表板中,两个图表面板的定义如下所示
对于简单的 CPU 统计信息,measurement 已命名。? 对于磁盘统计信息,每台服务器上的磁盘数量不同,因此代码使用 ic_sub() 来处理它。Grafana 可以计算子 measurement 磁盘的数量,并计算出如何显示该数量。请注意 “GROUP BY” 和 “ALIAS BY” 字段的使用。
免责声明 = 无
- 严格来说,“风险自负”。