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

背景

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

技术选型

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

  • 我们后台服务使用的都是长连接技术,tcpcopy只有等到下一次连接登录时才能完成实际的链路创建,这个在我们的环境中完全行不通,我们需要所有录制的信息都可以100%被回放;
  • 我们有很多的场景,库名和表名的映射都发生了变化,tcpcopy等现有技术无法解决我们的这个问题;
  • 我们希望在录制和回放的过程中,不但能够校验是否发生错误、响应延迟等,还能对response做校验,甚至是对两个数据库的数据一致性做校验;

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

功能分解

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

  • mysql协议理解:目前版本中只需理解登录、命令执行和连接关闭即可,非常简单;
  • 命令解析及输出:直接输出实际的sql文本命令,命令会标识出具体是哪个连接的命令及发送的时间;
  • 回放功能:支持原速回放和倍速回放;
  • 延迟和错误检测;

技术实现

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上进行了回放,共发现了两个问题:

  • TiKV的一个已经解决的bug,我司目前tidb的版本是2.1.9,当transfer leader时遇到了conf change时,会出现短暂的慢查询现象(连接会被TiDB占住一段时间)PR详见
  • nginx在做tcp proxy时,会偶尔出现连接卡死的现象,只有到达超时时间,连接被kill后,此连接的业务才会恢复。我司之前使用的是第三方的nginx_tcp_proxy_module的模块。后来把nginx的版本升级为1.17.1并使用nginx官方的stream模块,此问题也解决了;

未来

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

  • 可以边录制、边回放;
  • 可以对CRUD语句进行校验检测;
  • 可以定期的对于目标表数据进行一致性的校验;
  • 可以不仅仅支持mysql的协议;
  • 可以开源出来;
  • ...
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 摘要: 本文为今年年初 PingCAP 商业产品团队负责人刘寅在 TiDB DevCon2018 上分享的 《 T...
    nightwish夜愿阅读 11,738评论 0 11
  • 2018年才刚开始,平静了有一段时间的网约车市场却被跨界入局的美团点评给打破了。各大媒体都在报道美团正式在七大城市...
    木槿花开梧桐树来阅读 3,051评论 0 3
  • (原创作品) 最近气温有些低,非常怀念北方的集体供暖,想念我烟台海边的家,如果生活在在北方,正享受外面冰雪严寒,家...
    夏日莲阅读 4,077评论 9 14
  • 其实我不太能明白那些阿谀奉承的人为什么会比爱恨分明的人吃得开。
    Little绅士阅读 1,400评论 0 0
  • 程一电台 突如其来的雨淋湿整个夜 窗外的那个女孩 我知道她在等一个为她撑伞的男孩 而你却如这被雨淋湿玻璃上的雾气 ...
    DJ程一阅读 27,391评论 1 10

友情链接更多精彩内容