+86 135 410 16684Mon. - Fri. 10:00-22:00

使用zabbix_sender主动发送监控数据到zabbix server

【zabbix】使用zabbix_sender主动发送监控数据到zabbix server

使用zabbix_sender主动发送监控数据到zabbix server

通常来讲,大部分的zabbix key都是预先在server端定义好抓取数据的行为(比如抓取脚本,时间间隔,单位),然后由server端按照这个行为去向agent索要数据,或者 由agent按照这个行为往server端发送数据,但都逃不开“定时”这个概念,即抓取数据的时间间隔是固定的。有一种场景,需要监控的数据不是随时 (比如cpu,load)都能拿到的,而是由某些程序不定时的产生(比如crontab,某些用户触发的数据),这时候再用常规的zabbix key解决起来就不是那么的优雅,而且很难做到实时,理想的做法应该是程序产生监控数据的同时便将数据发送到zabbix server,这种做法类似于大名鼎鼎的newrelic,通过在程序端部署agent,然后发送数据到newrelic服务器。甚幸,zabbix提供类似的功能来帮助我们完成这种需求。
zabbix_sender:
zabbix提供的二进制程序,可以用来发送数据到zabbix server,具体用法如下:

>>> zabbix_sender -z 10.0.0.1 -p 10051 -s "rs_solrmaster" -k "get_number_of_solr_index_update" -o 4
需要注意的是get_number_of_solr_index_update这个key是需要事先创建在rs_solrmaster这台主机上的,并且是zabbix trapper类型的,并且是zabbix trapper类型的,并且是zabbix trapper类型的。
#usage: zabbix_sender [-Vhv] {[-zpsI] -ko | [-zpI] -T -i  -r} [-c ]
#Options:
#  -c --config                    配置文件的绝对路径
#  -z --zabbix-server             zabbix server或者zabbix proxy的ip地址
#  -p --port                      zabbix server或者zabbix proxy的端口,默认是10051
#  -s --host                      主机名,指的是zabbix里配制的主机名
#  -I --source-address            源ip,干嘛使得
#  -k --key                       zabbix trapper key名字
#  -o --value                     需要发送的监控项值
#  -i --input-file <input type="text" />   通过文件的方式批量发送数据,每行一个纪录,格式为hostname,key,value,使用空格分割,如果hostname有空格,使用双引号。
>>> cat trapper_key.txt
"zabbix server" key1 10
"zabbix proxy" key2 20
10.0.0.2 key3 30
>>> zabbix_sender -z 10.0.0.1 -i trapper_key.txt

如此,我们便可以随时随地的向zabbix发送监控数据了,由于zabbix只提供了zabbix_sender这个二进制程序来发送数据,如果要 在程序中调用,直接调用一个二进程程序是一种很丑陋的做法,那么有没有更优雅的办法呢(zabbix的api部分并没有trapper key的相关说明)。有,注意前方高能。
zabbix_sender跟zabbix_server的通信方式是tcp协议的,也就是说zabbix_sender发送给一个DATA段为固定格式 的tcp数据包给zabbix_server,然后server端解析并处理请求。所以这个DATA段的数据格式如果能确定,那我们就可以使用程序来模拟 zabbix_sender了。
先来看两篇文章:
zabbix sender协议的研究 | itnihao
zabbix sender 2.0 协议文档
从中可以整理出一个完整的zabbix_sender请求包的格式如下:

源端口 目标端口
32位序列号
32位确认号
4位首部长度 保留(6位) URG ACK PSH PST SYN FIN 16位窗口大小
16位校验和 16位紧急指针
可选项
Z B X D 协议版本 8位数据长度 请求或者响应的json数据

可以看到基本的zabbix_sender数据协议即为’Z”B”X”D’ + 1位的协议版本号 + 8位的数据长度 + json数据组成,如文档,请求的json数据格式为:

{
        "request":"sender data",
        "data":[
                {
                        "host":"Host name 1",
                        "key":"item_key",
                        "value":"33"},
                {
                        "host":"Host name 2",
                        "key":"item_key",
                        "value":"55"
                }
        ]
}

这样,基本的程序逻辑就出来了,即为:
♠ 构建符合zabbix_sender协议的数据包,格式为 ‘4sBqns’ (n为json串的长度)
♠ 发送数据包至zabbix_server的端口
♠ 处理响应
如下为一个python的实现示例:

#!/usr/bin/env python

import struct
import json
import socket


class zabbix_sender:
        def __init__(self,zbx_server_host,zbx_server_port):
                self.zbx_server_host=zbx_server_host
                self.zbx_server_port=zbx_server_port
                self.zbx_header='ZBXD'
                self.zbx_protocols_version=1
                self.zbx_send_value={'request':'sender data','data':[]}
        def adddata(self,host,key,value):
                add_data={'host':host,'key':key,'value':value}
                self.zbx_send_value['data'].append(add_data)
        #按照协议封装数据包
        def makesenddata(self):
                zbx_send_json=json.dumps(self.zbx_send_value)
                zbx_send_json_len=len(zbx_send_json)
                self.zbx_send_data=struct.pack("<4sBq"+str(zbx_send_json_len)+"s",'ZBXD',1,zbx_send_json_len,zbx_send_json)
        def send(self):
                self.makesenddata()
                zbx_server_socket=socket.socket()
                zbx_server_socket.connect((self.zbx_server_host,self.zbx_server_port))
                zbx_server_write_df=zbx_server_socket.makefile('wb')
                zbx_server_write_df.write(self.zbx_send_data)
                zbx_server_write_df.close()
                zbx_server_read_df=zbx_server_socket.makefile('rb')
                zbx_response_package=zbx_server_read_df.read()
                zbx_server_read_df.close()
                #按照协议解数据包
                zbx_response_data=struct.unpack("<4sBq"+str(len(zbx_response_package) - struct.calcsize("<4sBq"))+"s",zbx_response_package)
                return zbx_response_data[3]

if __name__ == '__main__':
        zabbix_sender=zabbix_sender('10.0.0.1',10051)
        zabbix_sender.adddata('solrmaster','get_number_of_solr_index_update',60)
        response=zabbix_sender.send()
        print response
北京 上海 天津 重庆 河北 山东 辽宁 黑龙江 吉林 甘肃 青海 河南 江苏 湖北 湖南 江西 浙江 广东 云南 福建 海南 山西 四川 陕西 贵州 安徽 广西 内蒙古 西藏 新疆 宁夏 澳门 香港 台湾