falcon-ping机制实现

ping监控用途与场景

用于检测主机是否能通,存活等

业内常用工具

一般都是linux下使用fping 命令,能快速识别出主机是否宕机,zabbix 就是集成的该工具,但是用第三方语言(py go等)调用该命令性能会较差,而且对资源消耗巨大,甚至会出现大量的僵尸进程(资源回收不过来),包括zabbix也并不是直接使用命令,而是集成了一些库文件。

go fast-ping模块

GITHUB地址https://github.com/tatsushid/go-fastping
基于icmp包实现的一个快速ping模块,目前版本的ping监控使用的是该工具,但经过对压测数据的分析,以及源码的阅读,发现存在以下问题:

如果要ping 多个IP 会共用一个icmp连接

这样也是该模块能“fast”的原因,虽然加快了速度,但是准确度也大打折扣,一个icmp连接如果要处理多个ping数据,一方面要频繁的进行上下文切换,对结果的准确性会产生很大的影响,另一方面 icmp没有类似tcp三次握手机制,本身就不可靠。比如他处理方法为:先同时发出n个ip的icmp请求(go协程),然后再挂起一个接收循环等待响应,响应一个处理,但由于是并发机制,再加上使用的是同一个通道,又没有三次握手保证数据的可靠性,当该通道突然同时接到大量数据时,难免会出现丢包!

超时机制

通过阅读源码发现,该模块内置了一个计数器(默认1s),计数器置位后,就会强行关闭通道。所以之前的做法是对所有的主机ping 30次,第一个1s内,ping完一部分,下一个1s,继续随机ping(因为map是无序的,所以每次读取都有可能是不同的ip),等于是30s内随机碰撞,来实现快速的ping,经过测试,发现一秒内,能ping 400台主机左右。

go-fastping核心代码

image.png

可以看到复用了一个conn

优化方案

修改这种共用通道的机制

改成单IP独享一个通道,毕竟icmp,不如TCP可靠,每个通道一个协程负责处理(每开辟500个协程 sleep 1秒,等待资源释放一部分),发现不通的,再次发起一次ping数据,确认是否真的异常

代码

放弃了go-fastping 重写了一个ping.go
ping.go

type pkg struct {
    conn     net.PacketConn
    ipv4conn *ipv4.PacketConn
    msg      icmp.Message
    netmsg   []byte
    id       int
    seq      int
    maxrtt   time.Duration
    dest     net.Addr
}

type ICMP struct {
    Addr    net.Addr
    RTT     time.Duration
    MaxRTT  time.Duration
    MinRTT  time.Duration
    AvgRTT  time.Duration
    Final   bool
    Timeout bool
    Down    bool
    Error   error
}

func (t *pkg) Send(ttl int) ICMP {
    var hop ICMP
    var err error
    t.conn, hop.Error = net.ListenPacket("ip4:icmp", "0.0.0.0")

    if nil != err {
        return hop
    }
    defer t.conn.Close()
    t.ipv4conn = ipv4.NewPacketConn(t.conn)
    defer t.ipv4conn.Close()
    hop.Error = t.conn.SetReadDeadline(time.Now().Add(t.maxrtt))
    if nil != hop.Error {
        return hop
    }
    if nil != t.ipv4conn {
        hop.Error = t.ipv4conn.SetTTL(ttl)
    }
    if nil != hop.Error {
        return hop
    }
    sendOn := time.Now()
    if nil != t.ipv4conn {
        _, hop.Error = t.conn.WriteTo(t.netmsg, t.dest)
    }
    if nil != hop.Error {
        return hop
    }
    buf := make([]byte, 1500)
    for {
        var readLen int
        readLen, hop.Addr, hop.Error = t.conn.ReadFrom(buf)
        if nerr, ok := hop.Error.(net.Error); ok && nerr.Timeout() {
            hop.Timeout = true
            return hop
        }
        if nil != hop.Error {
            return hop
        }
        var result *icmp.Message
        if nil != t.ipv4conn {
            result, hop.Error = icmp.ParseMessage(1, buf[:readLen])
        }
        if nil != hop.Error {
            return hop
        }
        switch result.Type {
        case ipv4.ICMPTypeEchoReply:
            if rply, ok := result.Body.(*icmp.Echo); ok {
                if t.id == rply.ID && t.seq == rply.Seq {
                    hop.Final = true
                    hop.RTT = time.Since(sendOn)
                    return hop
                }

            }
        case ipv4.ICMPTypeTimeExceeded:
            if rply, ok := result.Body.(*icmp.TimeExceeded); ok {
                if len(rply.Data) > 24 {
                    if uint16(t.id) == binary.BigEndian.Uint16(rply.Data[24:26]) {
                        hop.RTT = time.Since(sendOn)
                        return hop
                    }
                }
            }
        case ipv4.ICMPTypeDestinationUnreachable:
            if rply, ok := result.Body.(*icmp.Echo); ok {
                if t.id == rply.ID && t.seq == rply.Seq {
                    hop.Down = true
                    hop.RTT = time.Since(sendOn)
                    return hop
                }

            }
        }
    }

}

func RunPing(Addr string, maxrtt time.Duration, maxttl int, seq int) (float64, error) {
    var res pkg
    var err error
    res.dest, err = net.ResolveIPAddr("ip", Addr)
    if err != nil {
        return 0, errors.New("Unable to resolve destination host")
    }
    res.maxrtt = maxrtt
    //res.id = rand.Int() % 0x7fff
    res.id = rand.Intn(65535)
    res.seq = seq
    res.msg = icmp.Message{Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ID: res.id, Seq: res.seq}}
    res.netmsg, err = res.msg.Marshal(nil)
    if nil != err {
        return 0, err
    }
    pingRsult := res.Send(maxttl)

    return float64(pingRsult.RTT.Nanoseconds()) / 1e6, pingRsult.Error
}

调用方法

_, err := RunPing("127.0.0.1", 6 * time.Second,64, i)

测试结果

与 fping程序ping的结果对比后,完全能保持一致。

仓储地址

https://github.com/peng19940915/ping-falcon

其他

MAXTTL:为icmp报文在网络中达到目标主机要经过多少跳,该参数主要目的是防止报文在网络内无限传递下去,我们在这里设置的为64跳,超过64跳认为已经不通了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容

  • 个人认为,Goodboy1881先生的TCP /IP 协议详解学习博客系列博客是一部非常精彩的学习笔记,这虽然只是...
    贰零壹柒_fc10阅读 5,053评论 0 8
  • 0. 介绍 本文源自《图解TCP/IP》第四、五章读书笔记。一篇文章让你了解IP协议。阅读的时候,注意一般知识点结...
    天才木木阅读 5,145评论 0 14
  • 前言 这一篇文章主要围绕了IP协议,ICMP协议和UDP协议展开,希望可以在这里大概做一个总结,将《TCP/IP协...
    Noskthing阅读 3,087评论 5 57
  • 一、架构设计及环境规划: 架构设计图: 架构设计说明: 1. 基础架构为LAMP环境,采用keepalived实现...
    Bogon阅读 10,548评论 0 10
  • 夜深人静的往往是最吵的 所有不想听到的声音都清清楚楚 我听见厕所的水龙头在滴水,风把衣架吹的晃晃悠悠,楼下的狗在垃...
    二西与莫夫阅读 135评论 0 0