本文原創作者鮑光亞,京東商城基礎平臺部軟件開發工程師,經作者同意發表于本人博客,如需轉載需經本人同意。
目前累計服務客戶1000多家,積累了豐富的產品開發及服務經驗。以網站設計水平和技術實力,樹立企業形象,為客戶提供成都網站制作、網站建設、網站策劃、網頁設計、網絡營銷、VI設計、網站改版、漏洞修補等服務。創新互聯始終以務實、誠信為根本,不斷創新和提高建站品質,通過對領先技術的掌握、對創意設計的研究、對客戶形象的視覺傳遞、對應用系統的結合,為客戶提供更好的一站式互聯網解決方案,攜手廣大客戶,共同發展進步。
一、前言
我部門對數據庫的監控使用的是開源的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的哈希過程是先調用哈希函數計算鍵值對應的哈希值,然后用取余法確定槽位號。因此,取余計算時的除數就是槽位數,該數值取素數(因為素數可以做到最大程度上均勻散列)。在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)的基礎上稍微進行了改進。該函數采用乘積和位操作達到快速哈希的目的。具體實現如下:
按照以上函數,模擬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狀態,直到服務停止。
從上圖可以看出,每個共享內存段都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數據結構及并行計算實現
本文路徑:http://vcdvsql.cn/article30/gdicpo.html
成都網站建設公司_創新互聯,為您提供ChatGPT、微信公眾號、網站內鏈、虛擬主機、品牌網站建設、軟件開發
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯