您好,登錄后才能下訂單哦!
本文原創(chuàng)作者鮑光亞,京東商城基礎(chǔ)平臺(tái)部軟件開(kāi)發(fā)工程師,經(jīng)作者同意發(fā)表于本人博客,如需轉(zhuǎn)載需經(jīng)本人同意。
一、 前言
我部門(mén)對(duì)數(shù)據(jù)庫(kù)的監(jiān)控使用的是開(kāi)源的Zabbix系統(tǒng),目前監(jiān)控了上萬(wàn)臺(tái)主機(jī)。本文旨在通過(guò)分析Zabbix系統(tǒng)server端的數(shù)據(jù)結(jié)構(gòu)和并行計(jì)算的實(shí)現(xiàn)方法,嘗試探尋Zabbix系統(tǒng)server端的潛在擴(kuò)展能力,同時(shí)希望有助于在實(shí)際應(yīng)用過(guò)程中進(jìn)一步優(yōu)化運(yùn)行效率和穩(wěn)定性。
Zabbix系統(tǒng)采用server-proxy-agent架構(gòu),其server端的主要功能是收集監(jiān)控?cái)?shù)據(jù)并基于所收集的數(shù)據(jù)觸發(fā)報(bào)警動(dòng)作。在實(shí)際應(yīng)用中,zabbix有可能會(huì)監(jiān)控10000臺(tái)主機(jī)(host,由hostid唯一標(biāo)識(shí)),如果每臺(tái)主機(jī)設(shè)置50個(gè)監(jiān)控指標(biāo)(item,由itemid唯一標(biāo)識(shí)),并且每分鐘收集一次數(shù)據(jù),則一共有50萬(wàn)個(gè)item,每秒鐘需要接收并處理8333項(xiàng)數(shù)據(jù)(value),即vps(values per second)為8333。如果有三分之一的item設(shè)置了報(bào)警觸發(fā)器(trigger,由triggerid唯一標(biāo)識(shí)),則共有17萬(wàn)個(gè)trigger。
在以上情境中,為了保證監(jiān)控的有效性和及時(shí)性,zabbix接收到每個(gè)value后需要立即在50萬(wàn)個(gè)item中找到正確的item,并獲取該item的前一個(gè)值(previous value,last(),以便計(jì)算增量),或者計(jì)算前5分鐘內(nèi)的平均值(avg(5m)),以便根據(jù)觸發(fā)器表達(dá)式(trigger expression,由functionid唯一標(biāo)識(shí))判斷是否應(yīng)該觸發(fā)報(bào)警事件(event,由eventid唯一標(biāo)識(shí))。同時(shí)如果item返回值類(lèi)型為數(shù)字型,還需要計(jì)算該item在一個(gè)小時(shí)內(nèi)的平均值(value_avg)、最大值(value_max)、最小值(value_min)。按照上面的vps數(shù)據(jù),zabbix至少需要每秒鐘搜索8333*500000次。此外,item和trigger并不是靜態(tài)數(shù)據(jù),用戶(hù)隨時(shí)可能會(huì)增加、修改、刪除、禁用(disable)、啟用(enable)某些item和trigger,zabbix需要在處理value時(shí)查詢(xún)?cè)搃tem和trigger最新的狀態(tài)。如何在一秒時(shí)間內(nèi)完成如此大量的操作,zabbix給出的方案是: 哈希表。
在并行計(jì)算的軟件方面,由于Zabbix系統(tǒng)監(jiān)控的各個(gè)主機(jī)之間是相對(duì)獨(dú)立的,無(wú)論在任務(wù)還是在數(shù)據(jù)方面都非常便于計(jì)算的并行化。服務(wù)器硬件方面,我們實(shí)際使用的服務(wù)器結(jié)構(gòu)是2*8處理器+三級(jí)緩存+16G*8內(nèi)存+SSD硬盤(pán)+10Gbps網(wǎng)卡(數(shù)據(jù)庫(kù)、zabbix server和web服務(wù)共用)。Zabbix的并行計(jì)算主要采用的是共享內(nèi)存模式。
二、 Zabbix中的哈希表種類(lèi)
Zabbix使用的哈希表是鏈?zhǔn)焦1恚饕幸韵挛孱?lèi)(都是在共享內(nèi)存中分配空間):
1. Valuecache
Valuecache中包含兩個(gè)哈希表vc_cache->items(itemid作為鍵值進(jìn)行哈希)和vc_cache->strpool(字符串作為鍵值),用于存儲(chǔ)收集到的values(包括數(shù)字型和字符串型),每個(gè)item占用一個(gè)slot,每個(gè)槽位都是一個(gè)鏈表,鏈表節(jié)點(diǎn)存儲(chǔ)實(shí)際需要的信息。
Valuecache的哈希表在服務(wù)啟動(dòng)時(shí)創(chuàng)建,服務(wù)退出時(shí)銷(xiāo)毀,初始槽數(shù)為1009(1000之后的第一個(gè)素?cái)?shù)),隨著表中元素?cái)?shù)量的增加,槽數(shù)也會(huì)按照一定的規(guī)則增多。Valuecache可使用的最大空間由配置文件中的ValueCacheSize參數(shù)控制,允許的范圍是128K-64G。
2. Dbcache
Dbcache中的cache->trends哈希表,用于緩存trends表(每個(gè)item的小時(shí)平均值、最大值、最小值)的數(shù)據(jù)。Zabbix server的history_syncer進(jìn)程會(huì)持續(xù)接收來(lái)自agent或者proxy的數(shù)據(jù)后會(huì)將其加載到緩存中,同時(shí)更新cache->trends哈希表。該哈希表中的元素是ZBX_DC_TREND結(jié)構(gòu)體。
Cache->trends表中的數(shù)據(jù)時(shí)間超過(guò)整點(diǎn)時(shí)會(huì)被flush到數(shù)據(jù)庫(kù)中,例如10點(diǎn)之后會(huì)將9-10點(diǎn)之間的數(shù)據(jù)flush到數(shù)據(jù)庫(kù)中。
Cache->trends哈希表在服務(wù)啟動(dòng)時(shí)創(chuàng)建,初始槽數(shù)與vc_cache->items相同,為1009(1000之后的第一個(gè)素?cái)?shù))。Cache->trends哈希表的最大可用空間由配置文件中的TrendCacheSize參數(shù)控制,允許的范圍是128K-2G。
3. Dbconfig
Dbconfig緩存中存儲(chǔ)了多個(gè)與監(jiān)控有關(guān)的配置信息的哈希表,包括config->hosts、config->items、config->functions、config->triggers等等。配置信息哈希表的鍵值包括hostid、itemid、functionid、triggerid、triggerdepid、expressionid、globalmacroid、hostmacroid、hosttemplateid、interfaceid、host_inventory等,其中數(shù)量最多的往往是itemid、functionid和triggerid,會(huì)在數(shù)十萬(wàn)級(jí)別(以10000個(gè)host計(jì))。
以config->items為例,該哈希表的元素是ZBX_DC_ITEM結(jié)構(gòu)體。Config->items中的數(shù)據(jù)是從數(shù)據(jù)庫(kù)中查詢(xún)獲得的,zabbix server的configuration syncer進(jìn)程會(huì)周期性地從數(shù)據(jù)庫(kù)同步數(shù)據(jù)到緩存中。
Dbconfig緩存中的其他哈希表與config->items表類(lèi)似,都是從數(shù)據(jù)庫(kù)同步數(shù)據(jù),都是在服務(wù)啟動(dòng)時(shí)創(chuàng)建,初始槽數(shù)都是1009,并隨著數(shù)據(jù)量的增加動(dòng)態(tài)擴(kuò)展。整個(gè)dbconfig緩存可用空間大小由CacheSize參數(shù)決定,取值范圍為128K-8G。
4. Strpool
此處的strpool與vc_cache->strpool是相互獨(dú)立的兩個(gè)哈希表。此Strpool緩存用于存儲(chǔ)配置信息相關(guān)的字符串值,它與dbconfig共同分享CacheSize的空間(strpool占15%)。Strpool存儲(chǔ)的字符串包括host name、item key、item delay_flex、snmp community、snmp securityname、snmp passphrase、logitem format等數(shù)據(jù)。Zabbix需要使用host name等字符串時(shí),會(huì)首先在strpool中查找。
Strpool的哈希表初始槽數(shù)為1009。鍵值是字符串本身,哈希值是對(duì)字符串調(diào)用哈希函數(shù)的返回值。
5. 其他
除了以上哈希表,還有snmpidx、vmware service等哈希表。
三、 哈希表的實(shí)現(xiàn)
下面以config->items哈希表為例,說(shuō)明zabbix中哈希表的實(shí)現(xiàn)方法。
1. 數(shù)據(jù)結(jié)構(gòu)定義
Zabbix采用的是鏈?zhǔn)焦1恚1碇械拿總€(gè)slot都是一個(gè)鏈表。具體的數(shù)據(jù)結(jié)構(gòu)定義如下:
2.
槽數(shù)取值及負(fù)載因子
Zabbix的哈希過(guò)程是先調(diào)用哈希函數(shù)計(jì)算鍵值對(duì)應(yīng)的哈希值,然后用取余法確定槽位號(hào)。因此,取余計(jì)算時(shí)的除數(shù)就是槽位數(shù),該數(shù)值取素?cái)?shù)(因?yàn)樗財(cái)?shù)可以做到最大程度上均勻散列)。在config->items哈希表中,槽位數(shù)的初始值是1009,隨著數(shù)據(jù)量的增加,當(dāng)負(fù)載因子(元素?cái)?shù)/槽數(shù))達(dá)到0.8時(shí),會(huì)擴(kuò)充槽數(shù)量(擴(kuò)充為當(dāng)前數(shù)量的1.5倍以上,并取素?cái)?shù))。因此,負(fù)載因子總是保持在0.8和0.533之間。
按照以上規(guī)則,每次擴(kuò)展哈希表,其槽數(shù)如下表示。當(dāng)item數(shù)量為50萬(wàn)時(shí),槽數(shù)應(yīng)為670849。
序號(hào) | 理論值 | 素?cái)?shù)(槽數(shù)) | 允許的元素?cái)?shù) |
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. 哈希函數(shù)
Zabbix使用的哈希函數(shù)是在fnv-1a函數(shù)(http://www.isthe.com/chongo/tech/comp/fnv/index.html)的基礎(chǔ)上稍微進(jìn)行了改進(jìn)。該函數(shù)采用乘積和位操作達(dá)到快速哈希的目的。具體實(shí)現(xiàn)如下:
按照以上函數(shù),模擬620000個(gè)itemid的哈希過(guò)程(槽數(shù)取1006279),哈希效率如下:
總桶數(shù) | 1006279 |
空桶數(shù)量 | 543405 |
深度大于1的桶數(shù) | 127940 |
載荷因子 | 0.616131311 |
最大桶深 | 7 |
深桶占有值桶比例 | 0.276403514 |
深桶占總桶數(shù)比例 | 0.127141677 |
空桶占總數(shù)比例 | 0.540014251 |
四、 任務(wù)和數(shù)據(jù)的并行化
1. 任務(wù)的并行
Zabbix系統(tǒng)的任務(wù)基本上都是基于所監(jiān)控的host和item,各個(gè)host和item之間有較強(qiáng)的獨(dú)立性。為了并行化,Zabbix將任務(wù)拆分為相對(duì)獨(dú)立的子任務(wù),各個(gè)子任務(wù)由一個(gè)或者多個(gè)進(jìn)程來(lái)執(zhí)行。Zabbix server端的進(jìn)程劃分如下表所示:
啟動(dòng) 順序 | process title | 允許 進(jìn)程數(shù) | 默認(rèn)值 | 任務(wù) |
1 | configuration syncer | 1-1 | 1 | 從數(shù)據(jù)庫(kù)同步數(shù)據(jù)到Dbconfig緩存 |
2 | db watchdog | 1-1 | 1 | 周期性地檢查server端數(shù)據(jù)庫(kù)是否可用,如果不可用則發(fā)送報(bào)警信息 |
3 | poller #n | 0-1000 | 5 | 根據(jù)dbconfig中的數(shù)據(jù),從passive agent和snmp設(shè)備采集數(shù)據(jù),并flush到共享內(nèi)存cache->history中 |
4 | unreachable poller #n | 0-1000 | 1 | 當(dāng)設(shè)備處于unreachable狀態(tài)時(shí),周期性地polling設(shè)備 |
5 | trapper #n | 0-1000 | 5 | 從socket接收并處理active agent和active proxy發(fā)來(lái)的數(shù)據(jù)(json格式,zabbix通訊協(xié)議),并flush到共享內(nèi)存cache->history中 |
6 | icmp pinger #n | 0-1000 | 1 | 根據(jù)dbconfig中的數(shù)據(jù),批量采集icmpping相關(guān)的item數(shù)據(jù),并flush到共享內(nèi)存cache->history中 |
7 | alerter | 1-1 | 1 | 發(fā)送各種報(bào)警通知 |
8 | housekeeper | 1-1 | 1 | 周期性地刪除過(guò)期的歷史數(shù)據(jù) |
9 | timer #n | 1-1000 | 1 | 計(jì)算與時(shí)間相關(guān)的trigger表達(dá)式等 |
10 | node watcher | 1-1 | 1 | 處理與node之間的交互 |
11 | http poller #n | 0-1000 | 1 | 收集web監(jiān)控相關(guān)的數(shù)據(jù),并flush到共享內(nèi)存cache->history中 |
12 | discoverer #n | 0-250 | 1 | 按照指定規(guī)則掃描網(wǎng)絡(luò),自動(dòng)發(fā)現(xiàn)host、interface等 |
13 | history syncer #n | 1-100 | 4 | 將共享內(nèi)存cache->history中的數(shù)據(jù)批量更新到數(shù)據(jù)庫(kù)中,并flush到共享內(nèi)存vc_cache、cache->trends、config->items等中 |
14 | escalator | 1-1 | 1 | 當(dāng)報(bào)警操作需要分步連續(xù)執(zhí)行時(shí),控制各步驟之間的escalations |
15 | ipmi poller #n | 0-1000 | 0 | 與poller進(jìn)程類(lèi)似,處理ipmi items |
16 | java poller #n | 0-1000 | 0 | 與poller進(jìn)程類(lèi)似,處理JMX items |
17 | snmp trapper #n | 0-1 | 0 | 與trapper進(jìn)程類(lèi)似,處理snmp items |
18 | proxy poller #n | 0-250 | 1 | 與passive proxy交互,以設(shè)定的頻率獲取所需要的json格式數(shù)據(jù)并將數(shù)據(jù)flush到共享內(nèi)存cache->history中 |
19 | self-monitoring | 1-1 | 1 | 處理與zabbix自身運(yùn)行狀態(tài)相關(guān)的item信息,訪(fǎng)問(wèn)共享內(nèi)存中的collector變量 |
20 | vmware collector #n | 0-250 | 0 | 采集vmware虛擬機(jī)相關(guān)的數(shù)據(jù),并flush到共享內(nèi)存中 |
所有進(jìn)程中比較關(guān)鍵的進(jìn)程有兩類(lèi):poller/trapper類(lèi)進(jìn)程,用于采集數(shù)據(jù)并加載到共享內(nèi)存中;history syncer進(jìn)程,用于更新數(shù)據(jù)庫(kù)及觸發(fā)events和報(bào)警。邏輯上這兩類(lèi)任務(wù)是先后執(zhí)行的,首先要采集到數(shù)據(jù)然后才能觸發(fā)報(bào)警。而每類(lèi)任務(wù)的各個(gè)進(jìn)程之間是獨(dú)立的,多個(gè)poller/trapper進(jìn)程可以同時(shí)執(zhí)行,多個(gè)history syncer進(jìn)程也可以同時(shí)執(zhí)行。
2. Socket multiplexing對(duì)多進(jìn)程的支持
Zabbix監(jiān)控系統(tǒng)的數(shù)據(jù)最終來(lái)源是被監(jiān)控的主機(jī),數(shù)據(jù)通過(guò)socket監(jiān)聽(tīng)端口接收(監(jiān)聽(tīng)端口允許的最大連接數(shù)由操作系統(tǒng)決定)。Zabbix通過(guò)fork多個(gè)子進(jìn)程來(lái)共享同一個(gè)socket,在讀socket時(shí)則通過(guò)基于select()函數(shù)的multiplexing實(shí)現(xiàn)多進(jìn)程同時(shí)讀取。
按照10000個(gè)host,每分鐘采集一次數(shù)據(jù)(假設(shè)每個(gè)host上的所有item同時(shí)采集數(shù)據(jù),事實(shí)可能并非如此),平均每秒鐘有167個(gè)連接請(qǐng)求。
3. Mysql數(shù)據(jù)庫(kù)的讀寫(xiě)
Zabbix支持多種數(shù)據(jù)庫(kù),包括Mysql、Oracle、IBM DB2、PostgreSQL、SQLite,我們實(shí)際使用的是Mysql。為了保證數(shù)據(jù)的持續(xù)性,zabbix在觸發(fā)報(bào)警前會(huì)先將數(shù)據(jù)插入到數(shù)據(jù)庫(kù)中。History syncer進(jìn)程數(shù)允許最多100個(gè),每個(gè)進(jìn)程可以與數(shù)據(jù)庫(kù)建立獨(dú)立的連接,進(jìn)行數(shù)據(jù)更新。
五、 共享內(nèi)存與進(jìn)程間通信
1. 共享內(nèi)存的創(chuàng)建
共享內(nèi)存是進(jìn)程間通信中最簡(jiǎn)單并且速度最快的一種機(jī)制。Zabbix的進(jìn)程間通信主要采用共享內(nèi)存的方式,主進(jìn)程在fork出所有子進(jìn)程之前調(diào)用shmget創(chuàng)建共享內(nèi)存,并attach到地址空間中。
Zabbix調(diào)用shmget創(chuàng)建的共享內(nèi)存segment共有8個(gè),為config_mem、trend_mem、history_mem、history_text_mem、vc_mem、vmware_mem、strpool.mem_info、collector,分別用于dbconfig緩存、cache->trends數(shù)據(jù)、cache->history(數(shù)字和string)、vc_cache、vmware數(shù)據(jù)、strpool、監(jiān)控zabbix自身狀態(tài)的collector結(jié)構(gòu)。如果實(shí)際應(yīng)用中沒(méi)有啟用vmware,則只有7個(gè)共享內(nèi)存被attach到各子進(jìn)程的地址空間中,如下圖所示,這些共享內(nèi)存段將一直保持attach狀態(tài),直到服務(wù)停止。
從上圖可以看出,每個(gè)共享內(nèi)存段都attach到了553個(gè)進(jìn)程中,即zabbix server的每個(gè)進(jìn)程都可以訪(fǎng)問(wèn)所有七個(gè)共享內(nèi)存。
2. 信號(hào)量機(jī)制
Zabbix使用二進(jìn)制信號(hào)量機(jī)制來(lái)協(xié)調(diào)多個(gè)進(jìn)程對(duì)共享內(nèi)存的同時(shí)訪(fǎng)問(wèn),避免資源爭(zhēng)用。系統(tǒng)在創(chuàng)建共享內(nèi)存之前會(huì)調(diào)用semget函數(shù),創(chuàng)建一個(gè)包含13個(gè)信號(hào)量的信號(hào)量集,并將每個(gè)信號(hào)量的值初始化為1。各個(gè)信號(hào)量用于對(duì)不同的共享內(nèi)存進(jìn)行訪(fǎng)問(wèn)控制,具體如下所示:
# 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
當(dāng)進(jìn)程需要對(duì)某個(gè)共享內(nèi)存進(jìn)行寫(xiě)操作時(shí),會(huì)首先lock(調(diào)用semop函數(shù)將信號(hào)量-1),執(zhí)行寫(xiě)操作完畢后將再u(mài)nlock(將信號(hào)量+1)。如果執(zhí)行l(wèi)ock時(shí)信號(hào)量為0,則等待,直到信號(hào)量非0。Zabbix的信號(hào)量在釋放共享內(nèi)存時(shí)銷(xiāo)毀。
六、 聲明與結(jié)論
本文創(chuàng)作基于zabbix 2.2.10版本的源碼分析,歡迎批評(píng)指正。
Zabbix所采用的哈希函數(shù)效果比較理想。但在實(shí)際應(yīng)用中,仍然可以根據(jù)需要和資源情況對(duì)負(fù)載因子、槽數(shù)擴(kuò)展速度、槽數(shù)初值、哈希函數(shù)定義進(jìn)行改進(jìn)。
在Zabbix的并行計(jì)算方面,由于監(jiān)控系統(tǒng)的特點(diǎn),數(shù)據(jù)和任務(wù)之間有較強(qiáng)的獨(dú)立性,非常便于并行化。Zabbix通過(guò)多進(jìn)程+共享內(nèi)存實(shí)現(xiàn)并行,資源爭(zhēng)用問(wèn)題通過(guò)信號(hào)量進(jìn)行控制。從實(shí)際應(yīng)用效果來(lái)看,并行的性能非常理想。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。