Home/Server & Website Managed Operations/Actively Sending Monitoring Data to Zabbix Server Using zabbix_sender
Server & Website Managed Operations

Actively Sending Monitoring Data to Zabbix Server Using zabbix_sender

Generally speaking, most Zabbix keys are pre-defined on the server side to specify data collection behaviors (such as scripts, intervals, and units). The server then requests data from the agent according to these behaviors, or the agent sends data to the server. However, all of these rely on a “scheduled” concept, meaning the data collection interval is fixed. There is a scenario where the monitored data is not available at any time (like CPU or load) but is generated irregularly by certain programs (such as crontab jobs or user-triggered events). In such cases, using standard Zabbix keys is not elegant and makes real-time monitoring difficult. The ideal approach is to send monitoring data to the Zabbix server immediately when the program generates it. This method is similar to how New Relic works, by deploying an agent on the application side to send data to New Relic servers. Fortunately, Zabbix provides similar functionality to help meet this requirement.
Zabbix Sender:
Zabbix provides a binary executable called `zabbix_sender` that can be used to send data to the Zabbix server. The specific usage is as follows:

>>> zabbix_sender -z 10.0.0.1 -p 10051 -s "rs_solrmaster" -k "get_number_of_solr_index_update" -o 4
Note that get_number_of_solr_index_update is a key that must be created in advance on the host rs_solrmaster, and it must be of type zabbix trapper.
#usage: zabbix_sender [-Vhv] {[-zpsI] -ko | [-zpI] -T -i  -r} [-c ]
#Options:
#  -c --config                    Absolute path to the configuration file
#  -z --zabbix-server             IP address of the Zabbix server or Zabbix proxy
#  -p --port                      Port of the Zabbix server or Zabbix proxy, default is 10051
#  -s --host                      Host name, referring to the host name configured in Zabbix
#  -I --source-address            Source IP address
#  -k --key                       Name of the Zabbix trapper key
#  -o --value                     Value of the item to be sent
#  -i --input-file <input type="text" />   Batch send data via a file, with one record per line in the format hostname,key,value separated by spaces. If the hostname contains spaces, use double quotes.
>>> 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

Thus, we can send monitoring data to Zabbix anytime and anywhere. Since Zabbix only provides the `zabbix_sender` binary for sending data, calling a binary executable directly within a program is considered poor practice. Is there a more elegant solution? (Note that the Zabbix API documentation does not cover trapper keys). Yes, pay attention to what comes next.
The communication between `zabbix_sender` and `zabbix_server` uses the TCP protocol. This means `zabbix_sender` sends a TCP packet with a fixed-format DATA segment to `zabbix_server`, which then parses and processes the request. Therefore, if we can determine the data format of this DATA segment, we can simulate `zabbix_sender` using a program.
Let’s look at two articles first:
Research on Zabbix Sender Protocol | itnihao
Zabbix Sender 2.0 Protocol Documentation
From these, we can derive the complete format of a `zabbix_sender` request packet as follows:

Source Port Destination Port
32-bit Sequence Number
32-bit Acknowledgment Number
4-bit Header Length Reserved (6 bits) URG ACK PSH RST SYN FIN 16-bit Window Size
16-bit Checksum 16-bit Urgent Pointer
Options
Z B X D Protocol Version 8-bit Data Length JSON data for request or response

As seen, the basic Zabbix sender data protocol consists of ‘Z”B”X”D’ + 1-byte protocol version number + 8-byte data length + JSON data. According to the documentation, the JSON data format for the request is:

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

This gives us the basic program logic, which is:
♠ Construct a data packet conforming to the Zabbix sender protocol, with the format ‘4sBqns’ (where n is the length of the JSON string)
♠ Send the data packet to the Zabbix server port
♠ Process the response
Below is an example implementation in Python:

#!/usr/bin/env python

import struct
import json
import socket


class ZabbixSender:
        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 add_data(self,host,key,value):
                add_data={'host':host,'key':key,'value':value}
                self.zbx_send_value['data'].append(add_data)
        # Encapsulate data packet according to protocol
        def make_send_data(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.make_send_data()
                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()
                # Decode data packet according to protocol
                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=ZabbixSender('10.0.0.1',10051)
        zabbix_sender.add_data('solrmaster','get_number_of_solr_index_update',60)
        response=zabbix_sender.send()
        print response
FREE CHECKUP

Free server checkup

We will send a problem checklist and remediation advice within one business day — no obligation to engage.

135 410 16684
Weekdays Mon – Fri 10:00 – 22:00 (UTC+8) · 7×24 emergency response
Call now
CONTACT

Contact Us

How can we help?

* Required