這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
一個監控項目有個需求,會對一批網域名稱進行探測,這裡包括,丟包率,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 mainimport ( "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,謝謝。