一个监控项目有个需求,会对一批域名进行探测,这里包括,丢包率,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,谢谢。
有疑问加站长微信联系(非本文作者)