prometheus的网络ping监控exporter

Posted 姚__

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了prometheus的网络ping监控exporter相关的知识,希望对你有一定的参考价值。

轻量级的对网站和服务的ping监控插件,使用go语言实现
核心代码比较简单
github地址
已经有编译好的可执行文件。windows执行bat,linux执行sh
直接执行main -h 可以查看帮助

启动方法
./main -port 8889 -pingaddr www.baidu.com -count 4
启动后访问 127.0.0.1:8889/metrics
可以查看到输出指标,有无法访问次数和平均延迟统计信息

下面是代码,参考网上大佬的,我改造加了我的需求

package main

import (
	"bytes"
	"encoding/binary"
	"flag"
	"fmt"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"log"
	"net"
	"net/http"
	"strconv"
	"time"
)
var (
	opsQueued = prometheus.NewGaugeVec(
		prometheus.GaugeOpts
			Name:      "run_times",
			Help:      "Number of run times.",
		,
		[]string
			// Which user has requested the operation?
			"addr",
			// Of what type is the operation?
			"type",
		,
	)
	pingResult = prometheus.NewGauge(
		prometheus.GaugeOpts
		Name: "ping_avg_time",
		Help: "Time of the ping avg.",
		)
	pingLost = prometheus.NewGauge(
		prometheus.GaugeOpts
			Name: "ping_lost",
			Help: "lost of the ping.",
		)
)
func init() 
	// Metrics have to be registered to be exposed:
	prometheus.MustRegister(opsQueued)
	prometheus.MustRegister(pingResult)
	prometheus.MustRegister(pingLost)

const (
	MAX_PG = 2000
)

// 封装 icmp 报头
type ICMP struct 
	Type        uint8
	Code        uint8
	Checksum    uint16
	Identifier  uint16
	SequenceNum uint16


var (
	originBytes []byte
	githubPath string = "https://github.com"
	version string = "v1.0"
	listenAddress string
	help bool
	pingAddress string
	pingTimes int64
)

func init() 
	originBytes = make([]byte, MAX_PG)
	flag.StringVar(&listenAddress,"port","8888","this exporter listened port")
	flag.BoolVar(&help,"h",false,"help")
	flag.Int64Var(&pingTimes,"count",4,"ping count")
	flag.StringVar(&pingAddress,"pingaddr","www.baidu.com","pingaddr")


func CheckSum(data []byte) (rt uint16) 
	var (
		sum    uint32
		length int = len(data)
		index  int
	)
	for length > 1 
		sum += uint32(data[index])<<8 + uint32(data[index+1])
		index += 2
		length -= 2
	
	if length > 0 
		sum += uint32(data[index]) << 8
	
	rt = uint16(sum) + uint16(sum>>16)

	return ^rt


func Ping(domain string, PS, Count int) 
	var (
		icmp                      ICMP
		laddr                     = net.IPAddrIP: net.ParseIP("0.0.0.0") // 得到本机的IP地址结构
		raddr, _                  = net.ResolveIPAddr("ip", domain)        // 解析域名得到 IP 地址结构
		max_lan, min_lan, avg_lan float64
	)
	// 返回一个 ip socket
	conn, err := net.DialIP("ip4:icmp", &laddr, raddr)

	if err != nil 
		fmt.Println(`socket Error:`+err.Error())
		pingResult.Set(3000)
		pingLost.Set(100)
		return
	

	defer conn.Close()

	// 初始化 icmp 报文
	icmp = ICMP8, 0, 0, 0, 0

	var buffer bytes.Buffer
	binary.Write(&buffer, binary.BigEndian, icmp)
	//fmt.Println(buffer.Bytes())
	binary.Write(&buffer, binary.BigEndian, originBytes[0:PS])
	b := buffer.Bytes()
	binary.BigEndian.PutUint16(b[2:], CheckSum(b))

	//fmt.Println(b)
	fmt.Printf("\\n正在 Ping %s 具有 %d(%d) 字节的数据:\\n", raddr.String(), PS, PS+28)
	recv := make([]byte, 1024)
	ret_list := []float64

	dropPack := 0.0 /*统计丢包的次数,用于计算丢包率*/
	max_lan = 3000.0
	min_lan = 0.0
	avg_lan = 0.0

	for i := Count; i > 0; i-- 
		/*
			向目标地址发送二进制报文包
			如果发送失败就丢包 ++
		*/
		if _, err := conn.Write(buffer.Bytes()); err != nil 
			dropPack++
			time.Sleep(time.Second)
			continue
		
		// 否则记录当前得时间
		t_start := time.Now()
		conn.SetReadDeadline((time.Now().Add(time.Second * 3)))
		len, err := conn.Read(recv)
		/*
			查目标地址是否返回失败
			如果返回失败则丢包 ++
		*/
		if err != nil 
			dropPack++
			time.Sleep(time.Second)
			continue
		
		t_end := time.Now()
		dur := float64(t_end.Sub(t_start).Nanoseconds()) / 1e6
		ret_list = append(ret_list, dur)
		if dur < max_lan 
			max_lan = dur
		
		if dur > min_lan 
			min_lan = dur
		
		fmt.Printf("来自 %s 的回复: 大小 = %d byte 时间 = %.3fms\\n", raddr.String(), len ,dur)
		time.Sleep(time.Second)
	
	fmt.Printf("丢包率: %.2f%%\\n", dropPack/float64(Count)*100)
	if len(ret_list) == 0 
		avg_lan = 3000.0
	 else 
		sum := 0.0
		for _, n := range ret_list 
			sum += n
		
		avg_lan = sum / float64(len(ret_list))
	
	fmt.Printf("rtt 最短 = %.3fms 平均 = %.3fms 最长 = %.3fms\\n", min_lan, avg_lan, max_lan)
	pingResult.Set(Decimal(avg_lan))
	pingLost.Set(Decimal(dropPack/float64(Count)*100))


func Decimal(value float64) float64 
	value, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", value), 64)
	return value


func main() 
	flag.Parse()
	if help 
		flag.Usage()
		return
	
	if(pingTimes>100)
		fmt.Printf("最大次数不能超过100")
		return
	
	go func() 
		for
			fmt.Printf("开始执行ping \\n")
			Ping(pingAddress, 32, int(pingTimes))
			opsQueued.WithLabelValues(pingAddress, "ping").Inc()
			time.Sleep(10 * time.Second)
		
	()
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) 
		w.Write([]byte(`<html>
             <head><title>Ping_exporter</title></head>
             <body>
             <h1><a style="text-decoration:none" href='` + githubPath + `'>Ping_exporter</a></h1>
             <p><a href='\\metrics'>Metrics</a></p>
             <h2>Build</h2>
             <pre>` + version + `</pre>
             </body>
             </html>`))
	)
	// The Handler function provides a default handler to expose metrics
	// via an HTTP server. "/metrics" is the usual endpoint for that.
	http.Handle("/metrics", promhttp.Handler())
	log.Fatal(http.ListenAndServe(`:`+listenAddress, nil))


以上是关于prometheus的网络ping监控exporter的主要内容,如果未能解决你的问题,请参考以下文章

prometheus的网络ping监控exporter

Prometheus blackbox-exporter ICMP ping 监控配置所有目标都显示出来

使用prometheus+blackbox_exporter监控httpicmp网络性能

性能监控之 Blackbox_exporter+Prometheus+Grafana 实现网络探测

性能监控之 blackbox_exporter+Prometheus+Grafana 实现网络探测

Prometheus安装和配置node_exporter监控主机