使用gopacket来实现mysql的流量录制和回放

2019-07-15  本文已影响0人  mantuliu

背景

丰巢作为一家快速发展的科技公司,在我们平时的工作中,有很多的数据库迁移、改造、测试等工作。在我们之前做的一次异构数据库迁移过程中,我们投入了相对较多的人力。我本人一直在思考如何投入最少的人力,并且能使这个过程更加安全稳定,其中一个技术难点便是对于生产环境的流量在测试环境进行持续的回放,并在这个过程中发现问题所在。

技术选型

一开始本着不重复造轮子的原则,想在开源领域找到一款合适我们的产品。主要调研了tcpcopy框架,但是它不能满足我司的实际情况:

为了满足上面3个要求,我们开始了自研之路。在开始之前,我们需要一款能从数据链路层抓包的框架,我们选择了google开源的gopacket,因为我们之前使用gopacket实现过redis的实时命令分析工具,因此对它非常有信心。

功能分解

我对这款工具的定位是可以持续的输出业务价值,因此我希望它能最短的时间里便能产生作用,在制定第一期的功能列表时,本着不求大而全,只求有用的原则:

技术实现

gopacket使用

gopacket的使用非常简单,只需要在go.mod中引用gopacket的最新版本即可:

require github.com/google/gopacket v1.1.17

代码示例:

    devices, err := pcap.FindAllDevs()
    if err != nil {
        log.Fatal(err)
    }

    for _, deviceGet := range devices {
        for _, address := range deviceGet.Addresses {
            if address.IP.String() == *ip {
                device = deviceGet.Name
                break
            }
        }
    }
    if device == "" {
        fmt.Println("the ip don't match the network device. maybe you don't have the sudo authority.")
        return
    }
    
    handle, err = pcap.OpenLive(device, snapshot_len, true, timeout)
    if err != nil {
        log.Fatal(err)
    }
    defer handle.Close()

    var filter string = "tcp and dst host "
    filter = filter + *ip + " and dst port " + strconv.Itoa(*port)

    err = handle.SetBPFFilter(filter)
    if err != nil {
        log.Fatal(err)
    }
    
    packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
    var origin []byte
    for packet := range packetSource.Packets() {
        //do something
    }

倍速实现

关于回放逻辑中的倍速实现,我们需要知道几个参数:流量录制时的开始时间recordB、结束时间recordE、命令的执行时间recordC、流量回放时的开始时间replayB,通过这些参数我们就可以推断出命令在命令在回放时的实际执行时间replayC,公式如下,speed为放大的倍数,默认为1:

replayC = (recordC+(replayB-recordB)-replayB) /speed + replayB

效果

大家也可以看出,我们在第一期 实现中,逻辑都比较简单,开发时间也就只花了5天左右的时间,我们使用此工具对我司支付平台的生产环境mysql流量进行了录制,并在测试环境中的TiDB上进行了回放,共发现了两个问题:

未来

我对这个工具还是很期待的,我希望它能够在资源可控的条件下,实时跑在生产环境mysql的实例服务器上,总结一下后续的计划:

上一篇 下一篇

猜你喜欢

热点阅读