InfluxDB C 客户端库用于捕获统计数据
作者:社区 / 产品, 用例, 开发者
2021年2月23日
导航到
这是由 InfluxAce,IBM 技术员工,AIX 和 Linux 的 nmon 开发者 Nigel Griffiths 和 首次在 IBM 网站上发布 的文章的重发。
目前,没有官方的 InfluxDB C 语言客户端库。幸运的是,我想做的是捕捉 AIX 和 Linux 的操作系统性能统计。这个数据捕捉工具叫做“njmon”,在 Sourceforge 上是开源的。因此,我开发了一个小的函数库,包含 12 个函数,以便于保存数据,我想与大家分享。希望它对其他人也会有所帮助。
InfluxDB 文档中关于行协议格式的统计数据很好,但是 HTTP 要求模糊,您需要通过多次实验来发现细节。这个库还包括网络套接字处理层,这可能是第一次接触时比较棘手。
此 InfluxDB C 客户端库没有涵盖从 InfluxDB 中提取数据的功能(在 C 语言中处理 JSON 数据非常困难),也没有数据库管理功能,如创建 InfluxDB 数据库(我使用“influx”命令)。
环境
假设
- 您有 C 语言编译器。
- 您能编写一些简单的 C 语言程序。
- 您正在运行 InfluxDB。
以下操作系统和硬件的当前版本上测试了此代码
- AIX 7
- ppc64le (POWER8/9) 上的 Linux RHEL、SUSE 和 Ubuntu
- AMD64 (x86_64) 上的 Linux RHEL、SUSE 和 Ubuntu
代码可能适用于其他操作系统,因为 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,"运行")是硬编码的。这不是一个好主意,而且相当没有意义。希望这段代码能展示库函数与各种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())时才会建立与InfluxDB的连接,并在之后立即断开连接。
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行协议——非常简短的介绍
InfluxDB行协议需要以下部分
测量名称,标签,一个空格,键=值对,一个空格和时间戳
测量名称最好是有意义的,例如:cpu,memory,disk,transactions,temperature等。这些名称列在图形工具中,因此清晰的名称可以帮助选择正确的统计信息。
标签标识数据来源和使用来选择数据的特征。例如,服务器主机名(host=vm99)、数据的来源仪器(probe=a12)、应用程序名称(app=DB2)。
空格表示标签的结束和键值对的开始。键=值是实际数据,如“cpubusy”=56因此
- 格式:
测量值,标签 key=value 时间戳
CPU利用率,host=vm123 cpu_user=60,cpu_sys=15,cpu_wait=15,cpu_idle=10 时间戳
- ic 库允许 InfluxDB 添加时间戳(自1970年1月1日起的秒数),从而确保收集工具之间的时间一致性。
捕获您的统计信息
- 假设您知道如何在C程序中捕获您的实际数据。
- 通常数据来自操作系统的C库函数调用或与您安装的额外工具和应用程序一起提供的库。
- 文章开头的小型示例涵盖了基础知识。以下是详细说明
- ic_measure()
- 然后,根据数据格式保存数据的多个函数
- ic_long() 用于C语言长整型(大整数(技术上为64位长整型))
- ic_double() 用于C语言双精度浮点数
- ic_string() 用于C语言字符串(以零字符终止的字符数组)
- 最后使用 ic_measureend()
void ic_measure(char *measurement_name)
- 此函数命名测量(一组统计信息),并以对 ic_measureend() 的匹配调用结束。部分名称被保存在输出缓冲区中。
- 您可以有多个 ic_measure() 和 ic_measureend() 对来保存不同的资源。例如,计算机统计信息:CPU、内存、磁盘、网络等。
- 例如,ic_measure("CPU利用率");
void ic_measureend()
- 此函数用于标记当前测量的结束。
void ic_long(char *name, long long value)
- 此函数将名称和整数保存到输出缓冲区。
- 测量中可以有多个 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)
- 此函数将名称和双精度浮点数保存到输出缓冲区。
- 测量中可以有多个 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 = 浮点数" 的键,但该键已作为整数写入 InfluxDB,则整个测量将被拒绝。
- 当我认为数据显然是整数(例如运行队列号)时,我遇到了这样的问题。然后,后来意识到运行队列变化需要除以秒数。因此,它变成了一个浮点数。选项是删除数据库中的所有当前数据或将 "runqueue" 重命名为略微不同的名称,如 "runq"。
void ic_string(char *name, char *value)
- 此函数将名称和字符串保存到输出缓冲区。
- 测量中可以有多个 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重启以更新到更新的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)
}
始终在ic_push()之后直接调用sleep(),以便尽可能减小数据收集与ic_push()之间的时间。
如果您有多个相同类型的单元怎么办?
来自计算机统计领域的示例,您的笔记本电脑只有一个硬盘,但更大的服务器可能有数十个甚至数百个硬盘,每个服务器可能有不同数量的硬盘。这使得绘图变得很棘手,因为图表上可能有1到100多条线。CPU核心、网络、文件系统、网络缓冲区、Firbe通道适配器等等也是如此。
为了处理这个问题,我们在测量中有一个称为子测量统计的东西。
void ic_sub(char *sub_name)
- 此函数用于ic_measurement()和ic_measurementend()函数中。
- 在匹配ic_subend()之前,将sub_name添加到此测量的标签中。
- 在ic_sub()和ic_subend()之间,使用ic_long()、ic_double()和ic_string()保存数据。
void ic_subend()
- 结束子测量,即与之前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行协议的详细信息——如果您向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 - 简单示例代码,帮助您正确使用函数。包括启动函数然后是简单的测量和包含子数据的测量。
- 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的默认端口号 - 如果使用非标准端口号,请更改此端口号。
- "ic"是InfluxDB服务器中的数据库名(也称为桶)- 我建议快速测试时使用"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”的可执行二进制文件。
调试级别为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”数据库(使用ic)并列出已保存的测量值(显示测量值)。此示例输出是从运行ic.c文件中的示例代码生成的, 因此CPU和磁盘统计数据是伪造的数据。
[nag@silver2 ic]$ influx
Connected to https://127.0.0.1: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统计信息,测量值被命名。? 对于磁盘统计信息,每个服务器上都有不同数量的磁盘,因此代码使用ic_sub()来处理。Grafana可以计算子测量磁盘的数量,并确定如何显示该数量。注意“GROUP BY”和“ALIAS BY”字段的使用。
保修=无
- 严格“自行承担风险”。