从零写一个兼容MySQL/Oracle的Proxy中件间(三):MySQL协议捕获和转发

续: 从零写一个兼容MySQL/Oracle的Proxy中件间(一)《初识Oracle的通信协议》 从零写一个兼容MySQL/Oracle的Proxy中件间(二):SQL捕获和改写

1.过去的两天我们实现了以下功能]

开始动手:

步骤一:中件间可以同时支持MySQL和Oracle

中件间的配置应该放在哪,理论上是想放在MySQL或zk里,当配置有变更的时候,中件间获得变更,但这个实现有点麻烦,可能得写好久,就先一个本地的配置文件

准备一个配置文件

proxy]
proxytype = mysql
bind = 0.0.0.0:1106
server = 10.26.*.*:3307
isssl = false
iscatchquery = true
iscatchlogin = false
maxsquerysize = 4096


[proxybak]
#proxytype = oracle
#bind = 0.0.0.0:1106
#server = 10.26.*.*:1521
#isssl = false
#iscatchquery = true
#iscatchlogin = false
#maxsquerylsize = 4096

然后在通信进程中收到包时处理

func (t *Proxy) pipeSend(dstCon, srcCon *Conn, chSend chan int64) {
  defer pipeClose(dstCon)
  switch ProxyType {
  case "mysql":
    log.Printf("mysql:sqlPipeMySQL\n")
    sqlPipeMySQL(srcCon, dstCon)
  case "oracle":
    log.Printf("oracle:sqlPipeOracle\n")
    sqlPipeOracle(srcCon, dstCon)
  }
  chSend <- 0
}

步骤二:实现sqlPipeMySQL 方法

因为MySQL开源的原因,解析的方法往上到处都是,copy了一个过来。重点是包的第4个字节,当buffer[4]=3的时候,这个就是sql语句。

func sqlPipeMySQL(src, dst *Conn) {
  buffer := make([]byte, Bsize)
  client_ip, _ := ipPortFromNetAddr(src.conn.RemoteAddr().String())
  server_ip, _ := ipPortFromNetAddr(dst.conn.RemoteAddr().String())

  defer src.Close()
  for {
    n, err := src.Read(buffer)
    if err != nil {
      return
    }
    if n >= 5 {
      switch buffer[4] {
      case 1:
        log.Printf("抓到:%s-->%s:%s\n", client_ip, server_ip, "quit")
      case 4:
        log.Printf("抓到:%s-->%s:%s\n", client_ip, server_ip, "show databases")
      case 84:
        log.Printf("抓到:%s-->%s:%s\n", client_ip, server_ip, "conn prepare")
      case 133:
        log.Printf("抓到:%s-->%s:%s\n", client_ip, server_ip, "user connect")
      case 3:
        //SQL 语句
        log.Printf("抓到:%s-->%s:%s\n", client_ip, server_ip, removeNewLine(sqlInit(string(buffer[5:n]))))
      default:
        log.Printf("抓到:%s-->%s:buffer4:%v\n", client_ip, server_ip, buffer[4])
      }
    }
    _, err = dst.Write(buffer[0:n])
    if err != nil {
      return
    }
  }


再写个测试脚本:

#!/usr/bin/env python
## coding: utf-8
import cx_Oracle
import MySQLdb


def test_mysql():
    conn = MySQLdb.connect("127.0.0.1",port=1106,user="dboopreader",passwd= "dbooppassword", db="test")
    print("连接成功")
    curs = conn.cursor()
    sql = 'select 1 '
    curs.execute(sql)
    for result in curs:
        print(f"执行sql[ select 1 ]返回{str(result)}")
    sql = 'select 5 '
    curs.execute(sql)
    for result in curs:
        print(f"执行sql[ select 5 ]返回{str(result)}")
    curs.close()
    conn.close()
    print("连接关闭")

def test_oracle():
    conn = cx_Oracle.connect('dboopreader/dbooppassword@127.0.0.1:1106/tlionrdb')
    print("连接成功")
    curs = conn.cursor()
    sql = 'select 1 from dual'
    curs.execute(sql)
    for result in curs:
        print(f"执行sql[ select 1 from dual ]返回{str(result)}")
    sql = 'select 5 from dual'
    curs.execute(sql)
    for result in curs:
        print(f"执行sql[ select 5 from dual ]返回{str(result)}")
    curs.close()
    conn.close()
    print("连接关闭")

if __name__ == "__main__":
    test_mysql()
    #test_oracle()

步骤三:执行测试脚本

 ## python3.9 script/test.py
连接成功
执行sql[ select 1 ]返回(1,)
执行sql[ select 5 ]返回(5,)
连接关闭

同时服务端收到日志

2022/01/10 18:54:38 抓到:127.0.0.1-->10.26.*.*:buffer4:141
2022/01/10 18:54:38 抓到:127.0.0.1-->10.26.*.*:buffer4:48
2022/01/10 18:54:38 抓到:127.0.0.1-->10.26.*.*:set autocommit=0
2022/01/10 18:54:38 抓到:127.0.0.1-->10.26.*.*:select 1 
2022/01/10 18:54:38 抓到:127.0.0.1-->10.26.*.*:select 5 
2022/01/10 18:54:38 抓到:127.0.0.1-->10.26.*.*:quit

至此MySQL协议的 抓取/分析/转发 就做完了,跟Oracle的盲猜,MySQL的功能实现容易太多。

>> Home

51ak

2022/01/10

Categories: mysql oracle proxy 数据库代理 Tags: 原创 精品

《数据库工作笔记》公众号
扫描上面的二维码,关注我的《数据库工作笔记》公众号