写了一个 MySQL 代理

github 地址 github.com/moodrain/mysql-proxy

背景

因为之前主要写 php,知道 php 一次请求就需要重新连接一次 MySQL 会影响性能。后面也发现了一些例如 SMProxy、kingshard 等的代理库,这些库都非常完善,能在生产环境上使用,不用再造轮子了。但本着学习 golang 的目的,还是来尝试一下写 MySQL 代理。

简述

主要是利用 golang 协程的便利,创建多个协程与 MySQL 建立连接。当 php 处理完请求释放连接时,golang 保持这个连接,等到下一次 php 打开连接时复用这个连接。

主要代码如下

for i := 0; i < connCount; i++ {

    go func(proxy lib.ProxyConn) {

        // 在这里阻塞,首次连接时需要等待新的客户端请求
        proxy.NewClientConn(server)
        proxy.NewMysqlConn(mysqlUrl)

        // 首次连接需要完整的 MySQL 和客户端握手流程
        err := proxy.Handshake()
        if err != nil {
            proxy.Close()
        }

        go proxy.PipeMysql2Client()
        go proxy.PipeClient2Mysql()

        for {
            if !proxy.IsClientClose() {
                continue
            }
            // 当客户端关闭连接后,这里阻塞,代理等待新的客户端连接
            proxy.NewClientConn(server)
            // 因为代理与 MySQL 保持了连接,所以代理与客户端只需要假握手
            err := proxy.FakeHandshake()
            if err != nil {
                proxy.CloseClient()
            }
            go proxy.PipeClient2Mysql()
        }

    }(connList[i])

}

参考

MySQL Packet 结构

官方文档

  1. 一个 Packet 中前三个 byte 表示 Payload 的长度(所以最大长度是 ffffff 16,777,215 即 2的24次方-1)
  2. 第四个 byte 表示序列号,序列号从 0 开始递增,直到该命令完成后重置回 0
  3. 剩下的内容都属于 Payload
MySQL 协议认证流程

官方文档

  1. 客户端连接后, MySQL 发送 initial Handshake Packet
  2. 客户端处理后发送 Handshake Response Packet
  3. MySQL 认证通过后返回 OK_Packet,然后客户端开始发送查询等命令
忽略断开连接命令

官方文档

php 程序在处理完请求后会向 MySQL 发送 0x01 COM_QUIT 命令,该命令会使 MySQL 关闭连接,代理为了保持和复用连接,忽略该命令

备注

  1. 本例子只用于学习和验证 golang 写 MySQL 代理。因初学 golang,不能保证代码的正确和效率(测试了一下 php 连接 golang 代理 MySQL 反而效率更低了)。如果发现问题请不吝赐教
  2. 本例子只描述了 MySQL 协议的大概,并不完全准确,完整用法请查阅官方文档
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!