如何使用golang写一个zabbix_sender?

2023-10-08 00:10:26 浏览数 (3)

引言

2023年诺贝尔物理学奖颁发给皮埃尔·阿戈斯蒂尼、费伦茨·克劳斯和安妮•吕利耶,以表彰他们“为研究物质中的电子动力学而产生阿秒光脉冲的实验方法”。随着奖项颁布,阿秒物理学也同时被老百姓所知。相信在不久的将来,必然会出现类似摄像机的工具用以记录原子内部的运动“影片”。所以人类也正是在理论和实践中不断的进步。回想人类从石器时代到当下发达的科技,都离不开工具的不断演变和创新。再回到我们的话题中,在zabbix的工具箱中也有那么一个工具——zabbix_sender给数以万计的开发者提供能力。而通常情况下我们不需要自己写一个zabbix_sender,因为zabbix官方已经提供了一个这样的工具。那么这个工具是什么作用呢?官方给的介绍如下:

zabbix_sender is a command line utility for sending monitoring data to Zabbix server or proxy. On the Zabbix server an item of type Zabbix trapper should be created with corresponding key. Note that incoming values will only be accepted from hosts specified in Allowed hosts field for this item.

这是一个命令行工具,旨在发送监控数据到zabbix server或者proxy。本文我们就一探究竟其原理所在,并使用golang实现类似的功能的版本程序。

原理

Zabbix Sender 在启动时,会通过 TCP 连接与 Zabbix Server 建立连接。中间会有认证过程,认证成功之后就可以发送数据。

根据官方介绍,Zabbix server使用基于JSON的通信协议,也就是sender的数据是以json格式发送给server。因为是TCP协议,所以必须按照它规定的协议头发送。而且它要求协议头必须使用小端模式,同时数据包大小也是有相应的限制。

协议头结构如下(via):

Field

Size

Size(large packet)

Description

<PROTOCOL>

4

4

"ZBXD" or 5A 42 58 44

<FLAGS>

1

1

Protocol flags:0x01 - Zabbix communications protocol 0x02 - compression 0x04 - large packet

<DATALEN>

4

8

Data length.

<RESERVED>

4

8

When compression is used (0x02 flag) - the length of uncompressed data When compression is not used - 00 00 00 00

也就是<PROTOCOL><FLAGS><DATALEN><RESERVED><JSON DATA>

代码设计

所以知道了原理代码也就很好实现了,下面是我以前写的示例代码,供参考:

代码语言:go复制
// 模拟zabbix-sender发送数据包给zabbix-server
// copyright: 金鹏

package main

import (
	"encoding/binary"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"unsafe"
)

const (
	ConstHeader       = "ZBXD"
	ConstHeaderLength = len(ConstHeader)
	Version           = 1
)

//封包
func Packet(message []byte) []byte {
	return append(append(append([]byte(ConstHeader),
		IntToBytes(Version, 4)...),
		IntToBytes(len(message), 8)...), message...)

}

//整形转换成字节
func IntToBytes(n, x int) []byte {

	// 小端处理
	b := make([]byte, x)
	i := uint32(n)
	binary.LittleEndian.PutUint32(b, i)
	if x == 4 {
		var bx []byte
		bx = append(bx, b[0])
		return bx
	}
	return b
}

// 是否小端
func IsLittleEndian() bool {
	var i int32 = 0x01020304
	u := unsafe.Pointer(&i)
	pb := (*byte)(u)
	b := *pb
	return (b == 0x04)
}

// 请求数据结构
type ReqPt struct {
	Data    []map[string]string `json:"data"`
	Request string              `json:"request"`
}

func sender(conn net.Conn, endpoint, key, value string) {
	var data map[string]string
	data = make(map[string]string)
	data["host"] = endpoint
	data["value"] = value
	data["key"] = key
	var datas []map[string]string
	datas = append(datas, data)
	reqData := ReqPt{
		Data:    datas,
		Request: "sender data",
	}
	reqDataSlice, _ := json.Marshal(reqData)
	_, err := conn.Write(Packet(reqDataSlice))
	if err != nil {
		fmt.Println("bad packet ", err)
	}

	bys, err := ioutil.ReadAll(conn)
	if err != nil {
		fmt.Println("bad request ", err)
	}
	var reciveText = string(bys)

	fmt.Println("result:", reciveText)
	fmt.Println("send over")
}

func main() {
	if !IsLittleEndian() {
		fmt.Println("System Incompatibility")
		os.Exit(1)
	}

	server := flag.String("server", "127.0.0.1:10051", "zabbix server")
	endpoint := flag.String("endpoint", "localhost", "report host name or ip")
	key := flag.String("key", "cloud.test", "metric name")
	value := flag.String("value", "0", "metric value")
	help := flag.Bool("help", false, "help")
	flag.Parse()

	if *help {
		usage()
		os.Exit(1)
	}
	if len(os.Args) != 5 {
		usage()
		os.Exit(1)
	}

	tcpAddr, err := net.ResolveTCPAddr("tcp4", *server)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fatal error: %sn", err.Error())
		os.Exit(1)
	}

	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
		os.Exit(1)
	}

	defer conn.Close()
	fmt.Println("connect zabbix server success")
	sender(conn, *endpoint, *key, *value)
}

func usage() {
	fmt.Fprintf(os.Stderr, `gozbx-sender  version: gozbx-sender /1.0.0
Usage: gozbx-sender  [-server=server] [-endpoint=host] [-key=key] [-value=value]
Options:
`)
	flag.PrintDefaults()
}

使用go build 编译。

使用方法:

代码语言:javascript复制
gozbx-sender  version: gozbx-sender /1.0.0
Usage: gozbx-sender  [-server=server] [-endpoint=host] [-key=key] [-value=value]
Options:
  -endpoint string
        report host name or ip (default "localhost")
  -help
        help
  -key string
        metric name (default "cloud.test")
  -server string
        zabbix server (default "127.0.0.1:10051")
  -value string
        metric value

上述代码使用golang实现了zabbix sender协议,在发送的data中如果想加上时间参数也可以加上clock字段。

总结

上述简单介绍了什么是zabbix-sender,以及概要地讲述了sender发送数据到server之间的传输协议,并使用golang实现了sender的数据发送,仅供学习参考。在实际使用中,我们需要更深刻的理解其协议,包含权限认证等,可以将其封装成SDK,以便业务程序可以直接发送业务监控指标给zabbix server。因知识储备有限,如有不足,请评论区交流,不吝赐教。

我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表

0 人点赞