一个监控项目有个需求,会对一批域名全国的边缘节点进行探测,这里包括,丢包率,http 响应时间,探测频率大概时间是2min 一个周期。这里的域名大概有几百个甚至上千。因为是golang 写的调度和agent, 因此,这里探测丢包率是一个有意思的问题。因为目前git 上没有一个好用的支持multi-ping 的库包,或者多ping 有bug,我本身实现了一个。html
git 地址:https://github.com/caucy/batch_pinglinux
icmp 的报文头部一共是2+2+4+4+4 个字节。git
type ICMP struct { Type uint8 Code uint8 CheckSum uint16 Identifier uint16 SequenceNum uint16 }
这里 type 是icmp 类型,常见有发送报文头 Echo, 回收报文头 Echo Reply 等,更多类型 见 https://tools.ietf.org/html/rfc792 。 Code 进一步划分ICMP的类型,该字段用来查找产生错误的缘由;CheckSum 校验码部分,这个字段包含有从ICMP报头和数据部分计算得来的,用于检查错误的数据;而Identifier 一般为进程id,标识具体是哪一个进程发送的icmp 包;SequenceNum 标识发送包的顺序id。github
icmp 有个特色,listen 能收到其余进程ping 的结果,看下面例子:golang
package main import ( "log" "github.com/caucy/batch_ping" ) func main() { ipSlice := []string{} // ip list should not more than 65535 ipSlice = append(ipSlice, "3g.qq.com") bp, err := ping.NewBatchPinger(ipSlice, false) // true will need to be root if err != nil { log.Fatalf("new batch ping err %v", err) } bp.SetDebug(true) // debug == true will fmt debug log bp.SetCount(100) bp.Run() }
启动上面的进程,会连续ping 3g.qq.com,同时,再启动一个进程ping www.baidu.com , 日志会显示,收到了220.181.38.150 的icmp 包。并发
第一种是最简单的,也是大多数探针采用的方式:subprocess 。这个方式有个缺点,就是每一个任务会fork 一个进程,很是耗费耗费资源。app
第二种方式,我是这样想的,golang 有icmp 包,可以支持send and recive, 我直接起协程 去 收发,每一个协程和subprocess 同样,先发后等,这样不就好了?而后起一组协程池,这样并发也能控制。然而,上面例子已经提到了,listen 后的conn 能收到其余进程 ping 的结果,这样实现挺麻烦。优化
第三种方式,一个协程收,一个协程发。最后选择的是这种方式。ui
一个协程收,一个协程发,有什么比较麻烦地方?由于icmp 层只能标识seq,因此会出现icmp 包头相同的状况,同时,批量收发,很是容易出现丢包的状况。操作系统
使用示例:
package main import ( "log" "github.com/caucy/batch_ping" ) func main() { ipSlice := []string{} // ip list should not more than 65535 ipSlice = append(ipSlice, "2400:da00:2::29") //support ipv6 ipSlice = append(ipSlice, "baidu.com") bp, err := ping.NewBatchPinger(ipSlice, false) // true will need to be root if err != nil { log.Fatalf("new batch ping err %v", err) } bp.SetDebug(true) // debug == true will fmt debug log bp.SetSource("") // if hava multi source ip, can use one isp bp.OnFinish = func(stMap map[string]*ping.Statistics) { for ip, st := range stMap { log.Printf("\n--- %s ping statistics ---\n", st.Addr) log.Printf("ip %s, %d packets transmitted, %d packets received, %v%% packet loss\n", ip, st.PacketsSent, st.PacketsRecv, st.PacketLoss) log.Printf("round-trip min/avg/max/stddev = %v/%v/%v/%v\n", st.MinRtt, st.AvgRtt, st.MaxRtt, st.StdDevRtt) log.Printf("rtts is %v \n", st.Rtts) } } err = bp.Run() if err != nil { log.Printf("run err %v \n", err) } bp.OnFinish(bp.Statistics()) }
由于icmp 基于udp,时间间隔很是小,发送机器很是多的时候,会出现很是严重丢包,内核参数须要优化。
最后,文章不易,但愿你们点个star,试用。 https://github.com/caucy/batch_ping 。