实现支持批量 ping 的golang 库包

    一个监控项目有个需求,会对一批域名进行探测,这里包括,丢包率,http 响应时间,探测频率大概时间是2min 一个周期。这里的域名大概有几百个,后期可能上千。由于是golang 写的调度和agent, 所以,这里探测丢包率是一个有意思的问题。由于目前git 上没有一个好用的支持batch ping 的库包,参照其他人的实现,我自己实现了一个。

    该文章后续仍在不断的更新修改中, 请移步到原文地址http://dmwan.cc, git 地址:https://github.com/caucy/batch_ping

    最开始我并不是太明白icmp 协议,我的设想,是有这样几种实现方式。

    第一种是最简单的,也是大多数监控agent 采用的方式:subprocess 。这个方式有个缺点,就是每个任务会fork 一个进程,一个是耗费资源,第二个是太慢了;

    第二种方式,我是这样想的,golang 有icmp 包,能够支持send and recive, 我直接起协程 去 收发,每个协程和subprocess 一样,先发后等,这样不就行了?然后起一组协程池,这样并发也能控制。然而,too young too simple 。。。

   icmp 具体协议不多说,自己百度,主要注意下  id 字段和 seq 字段(大端序和小端序其实是一个字段)就行!下面着重说几个要点。

    第二种做法是行不通的,主要是因为这么几个原因:

     1, icmp 是ip 层上,tcp/udp 之下 的协议,无连接的,这个大多数人都知道;

     2,icmp 是通过原始套接字收发,这个需要root 权限。所以库启动需要授权

     3,原始套接字listen ,receive 的时候,是怎么区别是哪个进程发的,哪个进程该收?这个很关键,套接字在收的的时候,是内核直接转发,所有ping 自己机器的包,都能收到。什么意思?自己的进程,需要通过id (自己的进程号)标记这个是自己发出去的,收的时候,grep 掉那些不是自己发的的包。

    4, 进程怎么区分,收发的顺序,也就是我到底是第几个包丢了?这个根据seq 可以做到。

    那我们到底该怎么做?其实知道上面几点就思路很清晰了,避开不属于自己的包,注意收发顺序,就ok 了。主要实现,是将所有的域名或者ip 解析成ip 后放于一个map 中,一个协程,send icmp ,一个协程 receive icmp, 一个tick 控制时间间隔,一个tick 控制整体超时。最后提供下回调接口就ok。 

    下面是一个调用示例:

package main



import (

    "batch_ping/ping"

    "time"

    "fmt"

    "golang.org/x/net/icmp"

)



func main (){

    ipSlice := []string{}

    ipSlice = append(ipSlice, "122.228.74.183")

    ipSlice = append(ipSlice, "wwww.baidu.com")

     ipSlice = append(ipSlice, "baidu.com")

    ipSlice = append(ipSlice, "121.42.9.142")

    ipSlice = append(ipSlice, "121.42.9.141")

    ipSlice = append(ipSlice, "121.42.9.144")

    ipSlice = append(ipSlice, "121.42.9.145")

    ipSlice = append(ipSlice, "121.42.9.146")

    ipSlice = append(ipSlice, "121.42.9.147")

    ipSlice = append(ipSlice, "121.42.9.148")

    ipSlice = append(ipSlice, "121.42.9.149")

    ipSlice = append(ipSlice, "121.42.9.150")




    bp, err := ping.NewBatchPinger(ipSlice, 4, time.Second*1, time.Second*10, true)



    if err != nil {

        fmt.Println(err)

    }



    bp.OnRecv = func(pkt *icmp.Echo, srcAddr string) {

        fmt.Printf("recv icmp_id=%d, icmp_seq=%d, srcAddr %v\n",

            pkt.ID, pkt.Seq, srcAddr)

    }



    bp.OnFinish = func(stMap map[string]*ping.Statistics) {

        for ip, st := range stMap{

            fmt.Printf("\n--- %s ping statistics ---\n", st.Addr)

            fmt.Printf("ip %s, %d packets transmitted, %d packets received, %v%% packet loss\n",ip,

                st.PacketsSent, st.PacketsRecv, st.PacketLoss)

            fmt.Printf("round-trip min/avg/max/stddev = %v/%v/%v/%v\n",

                st.MinRtt, st.AvgRtt, st.MaxRtt, st.StdDevRtt)

        }



    }



    bp.Run()

}

    可以做的,还可以支持下ipv6 等等。如果觉得有用点个star,谢谢。

    

猜你喜欢

转载自my.oschina.net/u/2950272/blog/1805452