我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Zabbix数据结构及并行计算实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Zabbix数据结构及并行计算实现

本文原创作者鲍光亚,京东商城基础平台部软件开发工程师,经作者同意发表于本人博客,如需转载需经本人同意。

一、 前言

我部门对数据库的监控使用的是开源的Zabbix系统,目前监控了上万台主机。本文旨在通过分析Zabbix系统server端的数据结构和并行计算的实现方法,尝试探寻Zabbix系统server端的潜在扩展能力,同时希望有助于在实际应用过程中进一步优化运行效率和稳定性。

Zabbix系统采用server-proxy-agent架构,其server端的主要功能是收集监控数据并基于所收集的数据触发报警动作。在实际应用中,zabbix有可能会监控10000台主机(host,由hostid唯一标识),如果每台主机设置50个监控指标(item,由itemid唯一标识),并且每分钟收集一次数据,则一共有50万个item,每秒钟需要接收并处理8333项数据(value),即vps(values per second)为8333。如果有三分之一的item设置了报警触发器(trigger,由triggerid唯一标识),则共有17万个trigger。

在以上情境中,为了保证监控的有效性和及时性,zabbix接收到每个value后需要立即在50万个item中找到正确的item,并获取该item的前一个值(previous value,last(),以便计算增量),或者计算前5分钟内的平均值(avg(5m)),以便根据触发器表达式(trigger expression,由functionid唯一标识)判断是否应该触发报警事件(event,由eventid唯一标识)。同时如果item返回值类型为数字型,还需要计算该item在一个小时内的平均值(value_avg)、最大值(value_max)、最小值(value_min)。按照上面的vps数据,zabbix至少需要每秒钟搜索8333*500000次。此外,item和trigger并不是静态数据,用户随时可能会增加、修改、删除、禁用(disable)、启用(enable)某些item和trigger,zabbix需要在处理value时查询该item和trigger最新的状态。如何在一秒时间内完成如此大量的操作,zabbix给出的方案是: 哈希表。

在并行计算的软件方面,由于Zabbix系统监控的各个主机之间是相对独立的,无论在任务还是在数据方面都非常便于计算的并行化。服务器硬件方面,我们实际使用的服务器结构是2*8处理器+三级缓存+16G*8内存+SSD硬盘+10Gbps网卡(数据库、zabbix server和web服务共用)。Zabbix的并行计算主要采用的是共享内存模式。

二、 Zabbix中的哈希表种类

Zabbix使用的哈希表是链式哈希表,主要有以下五类(都是在共享内存中分配空间):

1. Valuecache

Valuecache中包含两个哈希表vc_cache->items(itemid作为键值进行哈希)和vc_cache->strpool(字符串作为键值),用于存储收集到的values(包括数字型和字符串型),每个item占用一个slot,每个槽位都是一个链表,链表节点存储实际需要的信息。

Valuecache的哈希表在服务启动时创建,服务退出时销毁,初始槽数为1009(1000之后的第一个素数),随着表中元素数量的增加,槽数也会按照一定的规则增多。Valuecache可使用的最大空间由配置文件中的ValueCacheSize参数控制,允许的范围是128K-64G。

2. Dbcache

Dbcache中的cache->trends哈希表,用于缓存trends表(每个item的小时平均值、最大值、最小值)的数据。Zabbix server的history_syncer进程会持续接收来自agent或者proxy的数据后会将其加载到缓存中,同时更新cache->trends哈希表。该哈希表中的元素是ZBX_DC_TREND结构体。

Cache->trends表中的数据时间超过整点时会被flush到数据库中,例如10点之后会将9-10点之间的数据flush到数据库中。

Cache->trends哈希表在服务启动时创建,初始槽数与vc_cache->items相同,为1009(1000之后的第一个素数)。Cache->trends哈希表的最大可用空间由配置文件中的TrendCacheSize参数控制,允许的范围是128K-2G。

3. Dbconfig

Dbconfig缓存中存储了多个与监控有关的配置信息的哈希表,包括config->hosts、config->items、config->functions、config->triggers等等。配置信息哈希表的键值包括hostid、itemid、functionid、triggerid、triggerdepid、expressionid、globalmacroid、hostmacroid、hosttemplateid、interfaceid、host_inventory等,其中数量最多的往往是itemid、functionid和triggerid,会在数十万级别(以10000个host计)。

以config->items为例,该哈希表的元素是ZBX_DC_ITEM结构体。Config->items中的数据是从数据库中查询获得的,zabbix server的configuration syncer进程会周期性地从数据库同步数据到缓存中。

Dbconfig缓存中的其他哈希表与config->items表类似,都是从数据库同步数据,都是在服务启动时创建,初始槽数都是1009,并随着数据量的增加动态扩展。整个dbconfig缓存可用空间大小由CacheSize参数决定,取值范围为128K-8G。

4. Strpool

此处的strpool与vc_cache->strpool是相互独立的两个哈希表。此Strpool缓存用于存储配置信息相关的字符串值,它与dbconfig共同分享CacheSize的空间(strpool占15%)。Strpool存储的字符串包括host name、item key、item delay_flex、snmp community、snmp securityname、snmp passphrase、logitem format等数据。Zabbix需要使用host name等字符串时,会首先在strpool中查找。

Strpool的哈希表初始槽数为1009。键值是字符串本身,哈希值是对字符串调用哈希函数的返回值。

5. 其他

除了以上哈希表,还有snmpidx、vmware service等哈希表。

三、 哈希表的实现

下面以config->items哈希表为例,说明zabbix中哈希表的实现方法。

1. 数据结构定义

Zabbix采用的是链式哈希表,哈希表中的每个slot都是一个链表。具体的数据结构定义如下:

2. Zabbix数据结构及并行计算实现    
槽数取值及负载因子

Zabbix的哈希过程是先调用哈希函数计算键值对应的哈希值,然后用取余法确定槽位号。因此,取余计算时的除数就是槽位数,该数值取素数(因为素数可以做到最大程度上均匀散列)。在config->items哈希表中,槽位数的初始值是1009,随着数据量的增加,当负载因子(元素数/槽数)达到0.8时,会扩充槽数量(扩充为当前数量的1.5倍以上,并取素数)。因此,负载因子总是保持在0.8和0.533之间。

按照以上规则,每次扩展哈希表,其槽数如下表示。当item数量为50万时,槽数应为670849。

序号

理论值

素数(槽数)

允许的元素数

0

1000

1009

806

1

1513

1523

1217

2

2284

2287

1828

3

3430

3433

2745

4

5149

5153

4121

5

7729

7741

6191

6

11611

11617

9292

7

17425

17431

13943

8

26146

26153

20921

9

39229

39229

31382

10

58843

58889

47110

11

88333

88337

70668

12

132505

132511

106007

13

198766

198769

159014

14

298153

298153

238521

15

447229

447233

357785

16

670849

670849

536678

17

1006273

1006279

805022

18

1509418

1509427

1207540

19

2264140

2264149

1811318

3. 哈希函数

Zabbix使用的哈希函数是在fnv-1a函数(http://www.isthe.com/chongo/tech/comp/fnv/index.html)的基础上稍微进行了改进。该函数采用乘积和位操作达到快速哈希的目的。具体实现如下:

Zabbix数据结构及并行计算实现

按照以上函数,模拟620000个itemid的哈希过程(槽数取1006279),哈希效率如下:

总桶数

1006279

空桶数量

543405

深度大于1的桶数

127940

载荷因子

0.616131311

最大桶深

7

深桶占有值桶比例

0.276403514

深桶占总桶数比例

0.127141677

空桶占总数比例

0.540014251

四、 任务和数据的并行化

1. 任务的并行

Zabbix系统的任务基本上都是基于所监控的host和item,各个host和item之间有较强的独立性。为了并行化,Zabbix将任务拆分为相对独立的子任务,各个子任务由一个或者多个进程来执行。Zabbix server端的进程划分如下表所示:

启动

顺序

process title

允许

进程数

默认值

任务

1

configuration syncer

1-1

1

从数据库同步数据到Dbconfig缓存

2

db watchdog

1-1

1

周期性地检查server端数据库是否可用,如果不可用则发送报警信息

3

poller #n

0-1000

5

根据dbconfig中的数据,从passive agent和snmp设备采集数据,并flush到共享内存cache->history中

4

unreachable poller #n

0-1000

1

当设备处于unreachable状态时,周期性地polling设备

5

trapper #n

0-1000

5

从socket接收并处理active agent和active proxy发来的数据(json格式,zabbix通讯协议),并flush到共享内存cache->history中

6

icmp pinger #n

0-1000

1

根据dbconfig中的数据,批量采集icmpping相关的item数据,并flush到共享内存cache->history中

7

alerter

1-1

1

发送各种报警通知

8

housekeeper

1-1

1

周期性地删除过期的历史数据

9

timer #n

1-1000

1

计算与时间相关的trigger表达式等

10

node watcher

1-1

1

处理与node之间的交互

11

http poller #n

0-1000

1

收集web监控相关的数据,并flush到共享内存cache->history中

12

discoverer #n

0-250

1

按照指定规则扫描网络,自动发现host、interface等

13

history syncer #n

1-100

4

将共享内存cache->history中的数据批量更新到数据库中,并flush到共享内存vc_cache、cache->trends、config->items等中

14

escalator

1-1

1

当报警操作需要分步连续执行时,控制各步骤之间的escalations

15

ipmi poller #n

0-1000

0

与poller进程类似,处理ipmi items

16

java poller #n

0-1000

0

与poller进程类似,处理JMX items

17

snmp trapper #n

0-1

0

与trapper进程类似,处理snmp items

18

proxy poller #n

0-250

1

与passive proxy交互,以设定的频率获取所需要的json格式数据并将数据flush到共享内存cache->history中

19

self-monitoring

1-1

1

处理与zabbix自身运行状态相关的item信息,访问共享内存中的collector变量

20

vmware collector #n

0-250

0

采集vmware虚拟机相关的数据,并flush到共享内存中

所有进程中比较关键的进程有两类:poller/trapper类进程,用于采集数据并加载到共享内存中;history syncer进程,用于更新数据库及触发events和报警。逻辑上这两类任务是先后执行的,首先要采集到数据然后才能触发报警。而每类任务的各个进程之间是独立的,多个poller/trapper进程可以同时执行,多个history syncer进程也可以同时执行。

2. Socket multiplexing对多进程的支持

Zabbix监控系统的数据最终来源是被监控的主机,数据通过socket监听端口接收(监听端口允许的最大连接数由操作系统决定)。Zabbix通过fork多个子进程来共享同一个socket,在读socket时则通过基于select()函数的multiplexing实现多进程同时读取。

按照10000个host,每分钟采集一次数据(假设每个host上的所有item同时采集数据,事实可能并非如此),平均每秒钟有167个连接请求。

3. Mysql数据库的读写

Zabbix支持多种数据库,包括Mysql、Oracle、IBM DB2、PostgreSQL、SQLite,我们实际使用的是Mysql。为了保证数据的持续性,zabbix在触发报警前会先将数据插入到数据库中。History syncer进程数允许最多100个,每个进程可以与数据库建立独立的连接,进行数据更新。

五、 共享内存与进程间通信

1. 共享内存的创建

共享内存是进程间通信中最简单并且速度最快的一种机制。Zabbix的进程间通信主要采用共享内存的方式,主进程在fork出所有子进程之前调用shmget创建共享内存,并attach到地址空间中。

Zabbix调用shmget创建的共享内存segment共有8个,为config_mem、trend_mem、history_mem、history_text_mem、vc_mem、vmware_mem、strpool.mem_info、collector,分别用于dbconfig缓存、cache->trends数据、cache->history(数字和string)、vc_cache、vmware数据、strpool、监控zabbix自身状态的collector结构。如果实际应用中没有启用vmware,则只有7个共享内存被attach到各子进程的地址空间中,如下图所示,这些共享内存段将一直保持attach状态,直到服务停止。

Zabbix数据结构及并行计算实现

从上图可以看出,每个共享内存段都attach到了553个进程中,即zabbix server的每个进程都可以访问所有七个共享内存。

2. 信号量机制

Zabbix使用二进制信号量机制来协调多个进程对共享内存的同时访问,避免资源争用。系统在创建共享内存之前会调用semget函数,创建一个包含13个信号量的信号量集,并将每个信号量的值初始化为1。各个信号量用于对不同的共享内存进行访问控制,具体如下所示:

# define ZBX_MUTEX_LOG 0

# define ZBX_MUTEX_NODE_SYNC 1

# define ZBX_MUTEX_CACHE 2

# define ZBX_MUTEX_TRENDS 3

# define ZBX_MUTEX_CACHE_IDS 4

# define ZBX_MUTEX_CONFIG 5

# define ZBX_MUTEX_SELFMON 6

# define ZBX_MUTEX_CPUSTATS 7

# define ZBX_MUTEX_DISKSTATS 8

# define ZBX_MUTEX_ITSERVICES 9

# define ZBX_MUTEX_VALUECACHE 10

# define ZBX_MUTEX_VMWARE 11

# define ZBX_MUTEX_SQLITE3 12

当进程需要对某个共享内存进行写操作时,会首先lock(调用semop函数将信号量-1),执行写操作完毕后将再unlock(将信号量+1)。如果执行lock时信号量为0,则等待,直到信号量非0。Zabbix的信号量在释放共享内存时销毁。

六、 声明与结论

本文创作基于zabbix 2.2.10版本的源码分析,欢迎批评指正。

Zabbix所采用的哈希函数效果比较理想。但在实际应用中,仍然可以根据需要和资源情况对负载因子、槽数扩展速度、槽数初值、哈希函数定义进行改进。

在Zabbix的并行计算方面,由于监控系统的特点,数据和任务之间有较强的独立性,非常便于并行化。Zabbix通过多进程+共享内存实现并行,资源争用问题通过信号量进行控制。从实际应用效果来看,并行的性能非常理想。

Zabbix数据结构及并行计算实现

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Zabbix数据结构及并行计算实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android实践(计算器的数据结构实现)

新的知识,新的开始。 接下来一起探讨使用Android技术解决计算器诸多问题,首先这个方法并不是适合所有人,有数据结构基础的同学可以稍微看看。 一般实现Android计算器都是只能进行例如 x + y = z的操作,但是需要实现类似于a +
2022-06-06

Java数据结构中如何进行并查集的实现

这篇文章跟大家分析一下“Java数据结构中如何进行并查集的实现”。内容详细易懂,对“Java数据结构中如何进行并查集的实现”感兴趣的朋友可以跟着小编的思路慢慢深入来阅读一下,希望阅读后能够对大家有所帮助。下面跟着小编一起深入学习“Java数
2023-06-28

Java数据结构之栈与综合计算器的实现

这篇文章主要为大家详细介绍了Java数据结构中栈与综合计算器的实现,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解一下
2022-11-13

详解常用查找数据结构及算法(Python实现)

一、基本概念 查找(Searching)就是根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)。 查找表(Search Table):由同一类型的数据元素(或记录)构成的集合关键字(Key):数据元素中某个数据项的值
2022-06-04

Java数据结构之KMP算法详解以及代码实现

KMP算法是一种改进的字符串匹配算法,核心是利用之前的匹配失败时留下的信息,选择最长匹配长度直接滑动,从而减少匹配次数。本文主要介绍了KMP算法的原理与实现,需要的可以参考一下
2022-12-08

大数据文本并行计算实现方式是什么

本篇文章为大家展示了大数据文本并行计算实现方式是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。对于大数据文件的处理,可以充分利用现代计算机的多核CPU,实施多线程并行计算,从而达到提速的目的。然
2023-06-03

Java数据结构之KMP算法的实现

这篇文章主要为大家详细介绍了Java数据结构中KMP算法的原理与实现,文中的示例代码讲解详细,对我们学习Java有一定的帮助,需要的可以参考一下
2022-11-21

Java数据结构之KMP算法怎么实现

这篇文章主要讲解了“Java数据结构之KMP算法怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java数据结构之KMP算法怎么实现”吧!暴力匹配算法(Brute-Force,BF)这
2023-07-04

Cassandra数据的分布式计算和并行处理怎么实现

Cassandra是一个分布式数据库系统,它支持并行处理和分布式计算。要实现Cassandra数据的分布式计算和并行处理,可以采用以下几种方法:数据分片:Cassandra通过数据分片将数据分布在多个节点上,每个节点负责存储和处理一部分数据
Cassandra数据的分布式计算和并行处理怎么实现
2024-05-11

Beam怎么实现数据的并行处理和分布式计算

Beam是一个用于实现数据处理管道的统一编程模型,它可以在不同的运行环境中进行数据的并行处理和分布式计算。下面是Beam实现数据的并行处理和分布式计算的一般步骤:编写Beam管道:首先,开发人员需要编写一个Beam管道,定义数据的输入源、数
Beam怎么实现数据的并行处理和分布式计算
2024-03-15

C++并发编程:如何进行并发数据结构的线程安全设计?

线程安全并发数据结构设计:实现方式:原子类型和互斥锁原子类型:确保多个访问不可分割,保证数据一致性。互斥锁:限制一次一个线程访问共享数据,防止并发数据损坏。实例:线程安全队列展示了使用互斥锁实现的线程安全数据结构。C++并发编程:线程安全并
C++并发编程:如何进行并发数据结构的线程安全设计?
2024-05-03

设计与实现Golang中链表的数据结构

Golang中链表数据结构的设计与实现引言:链表是一种常见的数据结构,用于存储一系列的节点。每个节点包含数据和指向下一个节点的指针。在Golang中,我们可以通过使用结构体和指针来实现链表。链表的设计与结构定义在Golang中,我们可以
设计与实现Golang中链表的数据结构
2024-01-29

编程热搜

目录