天天看点

【原创】modb 功能设计之“支持部分MySQL客户端协议”-1

      最初的想法是,rabbitmq 客户端从 queue 消费到了包含 sql 语句的消息后,需要提取并分析该 sql 后,通过 mysql 协议再要求数据库执行该 sql 语句。这就要求我这个 demo 必须实现了 mysql 客户端侧所需的协议部分。要实现这个,有三种途径:   

利用公司已有的 dbi 的库实现 mysql 访问

基于 mysqlclient 库做开发实现 mysql 客户端侧协议

自己实现 mysql 客户端侧需要的协议

我的选择: 

      不打算用公司的 dbi 库,首先,该库基于了其他的公司库,而我并不想使用;其次,该库封装的也不是很好,之前我还查出了 4 处崩溃,不排除还有其他问题;最后,该库其实也是在 mysqlclient 上做的封装,和上面第 2条其实是一样的。所以,最终我决定选择第 2 种方式,自己搞个“山头”出来。

ps:其实开始的时候也想过采用上面的第 3 种方式,自己从底层开始实现如何支持 mysql 客户端协议,结果“呵呵”了,难度和工作量不是一般的大~~  

确定基于什么来做开发后,该做下一步目标选择了:

直接使用 mysqlclient 提供的 api 实现常规阻塞式调用能满足我的要求么?

是否需要基于 libevent 实现事件驱动式的调用模式?难度大么? 

是否有合适的开源 mysql 客户端实现来启发我的思路?供我参考? 

      不要以为我啥都知道,可以坦白的讲,做这个开发之前,我对上述的问题一无所知,不过我从不担心自己的学习能力, 于是乎开始各种搜索…… 

可以参考的客户端实现(似乎)有: 

4.5.1. mysql — the mysql command-line tool 

4.5.2. mysqladmin — client for administering a mysql server 

4.5.3. mysqlcheck — a table maintenance program 

4.5.4. mysqldump — a database backup program 

4.5.5. mysqlimport — a data import program 

4.5.6. mysqlshow — display database, table, and column information 

4.5.7. mysqlslap — load emulation client 

      其中 mysql、mysqladmin、mysqlshow 和 mysqlslap 看起来很值得研究。 于是开始研究如何在 linux下用 c 语言 api 连接 mysql 数据库。 

      在 mysql 官网上找到了如下内容,对基于 mysql c api 实现客户端应用程序的说明(个人总结) 

================================ 

mysql c api 为客户端应用程序提供了用于和 mysql server 通信使用的基于 c 的 api。 

提供了两种版本的库供使用:libmysqlclient(客户端版本)和 libmysqld(嵌入式服务器版本)。 

在 unix (以及 unix-like)系统上,静态库为 libmysqlclient.a,动态库为 libmysqlclient.so 。 

带 _r 后缀的库的含义:在 mysql 5.5 以前,带 _r 后缀的库为 thread-safe (re-entrant) 的库,否则,则不是。而在 mysql 5.5 版本之后,带不带 _r 后缀都是 thread-safe (re-entrant) 的库了,所以理论上讲,已经不需要带 _r 的库了。 

22.8.2. simultaneous mysql server and mysql connector/c installations 

这个主要讲同时安装 mysql server 和 mysql connector/c 的问题,和我要搞的事情无关 

22.8.3. example c api client programs 

这个本应该给出如何使用 c api 来编写客户端程序的,但是其只是告诉你可以参考 client 文件夹下的源码。大概扫了一下,可以参考的,且代码量较小的是 mysqlshow.c 。 

22.8.4. building and running c api client programs 

这个主要讲如何构建和运行 c api 的客户端程序了,主要分为3部分说明: 

22.8.4.1. building c api client programs 

示例说明如何编译客户端程序 

shell> gcc -c `mysql_config --cflags` progname.c 

shell> gcc -o progname progname.o `mysql_config --libs` 

22.8.4.2. writing c api threaded client programs 

讨论如何在调用 c api 的时候保证线程安全 

22.8.4.3. running c api client programs 

避免由于系统升级导致的头文件 mysql.h 和库 libmysqlclient.a 的不匹配问题,出现问题则需要使用新的头文件和库重新编译。若依赖动态库,当出现主版本好变更的时候(如 libmysqlclient.so.17 到 libmysqlclient.so.18),则需要重新编译。 

22.8.5. c api data structures 

这里开始介绍各种数据结构(略) 

      好吧,反正哪一个对我来说都是陌生的,那么就研究其中的 mysqlshow 试试看。 首先要干的事情就是,自己要能成功编译 mysqlshow 。

(...省略中间过程中的唧唧歪歪...) 

自建工程目录如下 

<a href="http://my.oschina.net/moooofly/blog/185491#">?</a>

1

2

3

4

5

6

7

<code>[root@betty mysql_client_test]</code><code># ll</code>

<code>总计 116</code>

<code>-rw-r--r-- 1 root root   4130 10-25 14:33 client_priv.h</code>

<code>-rw-r--r-- 1 root root   2124 10-25 15:01 my_default.h</code>

<code>-rw-r--r-- 1 7161 wheel 25393 10-25 15:03 mysqlshow.c</code>

<code>-rw-r--r-- 1 root root   1686 10-25 15:01 welcome_copyright_notice.h</code>

<code>[root@betty mysql_client_test]</code><code>#</code>

成功编译

8

9

<code>[root@betty mysql_client_test]</code><code># gcc mysqlshow.c -o mysqlshow `mysql_config --cflags` `mysql_config --libs` -ddbug_off </code>

<code>-rwxr-xr-x 1 root root  66953 10-25 16:23 mysqlshow</code>

执行

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

<code>[root@betty mysql_client_test]</code><code># ./mysqlshow --help</code>

<code>.</code><code>/mysqlshow</code>  <code>ver 9.10 distrib 5.6.10,</code><code>for</code> <code>linux (x86_64)</code>

<code>copyright (c) 2000, 2013, oracle and</code><code>/or</code> <code>its affiliates. all rights reserved.</code>

<code>oracle is a registered trademark of oracle corporation and</code><code>/or</code> <code>its</code>

<code>affiliates. other names may be trademarks of their respective</code>

<code>owners.</code>

<code>shows the structure of a mysql database (databases, tables, and columns).</code>

<code>usage: .</code><code>/mysqlshow</code> <code>[options] [database [table [column]]]</code>

<code>if last argument contains a shell or sql wildcard (*,?,% or _)</code><code>then</code> <code>only</code>

<code>what's matched by the wildcard is shown.</code>

<code>if no database is given</code><code>then</code> <code>all matching databases are shown.</code>

<code>if no table is given,</code><code>then</code> <code>all matching tables</code><code>in</code> <code>database are shown.</code>

<code>if no column is given,</code><code>then</code> <code>all matching columns and column types</code><code>in</code> <code>table</code>

<code>are shown.</code>

<code>default options are</code><code>read</code> <code>from the following files</code><code>in</code> <code>the given order:</code>

<code>/etc/my</code><code>.cnf</code><code>/etc/mysql/my</code><code>.cnf</code><code>/usr/local/mysql/etc/my</code><code>.cnf ~/.my.cnf </code>

<code>the following</code><code>groups</code> <code>are</code><code>read</code><code>: mysqlshow client</code>

<code>the following options may be given as the first argument:</code>

<code>--print-defaults        print the program argument list and</code><code>exit</code><code>.</code>

<code>--no-defaults           don't</code><code>read</code> <code>default options from any option</code><code>file</code><code>,</code>

<code>                        </code><code>except</code><code>for</code> <code>login</code><code>file</code><code>.</code>

<code>--defaults-</code><code>file</code><code>=</code><code>#       only read default options from the given file #.</code>

<code>--defaults-extra-</code><code>file</code><code>=</code><code># read this file after the global files are read.</code>

<code>--defaults-group-suffix=</code><code>#</code>

<code>                        </code><code>also</code><code>read</code> <code>groups</code> <code>with concat(group, suffix)</code>

<code>--login-path=</code><code>#          read this path from the login file.</code>

<code>  </code><code>--bind-address=name ip address to bind to.</code>

<code>  </code><code>-c, --character-sets-</code><code>dir</code><code>=name </code>

<code>                      </code><code>directory</code><code>for</code> <code>character</code><code>set</code> <code>files.</code>

<code>  </code><code>--default-character-</code><code>set</code><code>=name </code>

<code>                      </code><code>set the default character</code><code>set</code><code>.</code>

<code>  </code><code>--count             show number of rows per table (may be slow</code><code>for</code> <code>non-myisam</code>

<code>                      </code><code>tables).</code>

<code>  </code><code>-c, --compress      use compression</code><code>in</code> <code>server</code><code>/client</code> <code>protocol.</code>

<code>  </code><code>-</code><code>#, --debug[=name]  output debug log. often this is 'd:t:o,filename'.</code>

<code>  </code><code>--debug-check       check memory and</code><code>open</code> <code>file</code> <code>usage at</code><code>exit</code><code>.</code>

<code>  </code><code>--debug-info        print some debug info at</code><code>exit</code><code>.</code>

<code>  </code><code>--default-auth=name default authentication client-side plugin to use.</code>

<code>  </code><code>-?, --help          display this help and</code><code>exit</code><code>.</code>

<code>  </code><code>-h, --host=name     connect to host.</code>

<code>  </code><code>-i, --status        shows a lot of extra information about each table.</code>

<code>  </code><code>-k, --keys          show keys</code><code>for</code> <code>table.</code>

<code>  </code><code>-p, --password[=name] </code>

<code>                      </code><code>password to use when connecting to server. if password is</code>

<code>                      </code><code>not given, it's solicited on the</code><code>tty</code><code>.</code>

<code>  </code><code>--plugin-</code><code>dir</code><code>=name   directory</code><code>for</code> <code>client-side plugins.</code>

<code>  </code><code>-p, --port=</code><code>#        port number to use for connection or 0 for default to, in</code>

<code>                      </code><code>order of preference, my.cnf, $mysql_tcp_port,</code>

<code>                      </code><code>/etc/services</code><code>, built-</code><code>in</code> <code>default (3306).</code>

<code>  </code><code>--protocol=name     the protocol to use</code><code>for</code> <code>connection (tcp, socket, pipe,</code>

<code>                      </code><code>memory).</code>

<code>  </code><code>-t, --show-table-</code><code>type</code> 

<code>                      </code><code>show table</code><code>type</code> <code>column.</code>

<code>  </code><code>-s, --socket=name   the socket</code><code>file</code> <code>to use</code><code>for</code> <code>connection.</code>

<code>  </code><code>-u, --user=name     user</code><code>for</code> <code>login</code><code>if</code> <code>not current user.</code>

<code>  </code><code>-</code><code>v</code><code>, --verbose       more verbose output; you can use this multiple</code><code>times</code> <code>to</code>

<code>                      </code><code>get even</code><code>more</code> <code>verbose output.</code>

<code>  </code><code>-v, --version       output version information and</code><code>exit</code><code>.</code>

<code>variables (--variable-name=value)</code>

<code>and boolean options {false|true}  value (after reading options)</code>

<code>--------------------------------- ----------------------------------------</code>

<code>bind-address                      (no default value)</code>

<code>character-sets-</code><code>dir</code>                <code>(no default value)</code>

<code>default-character-</code><code>set</code>             <code>auto</code>

<code>count                             false</code>

<code>compress                          false</code>

<code>debug-check                       false</code>

<code>debug-info                        false</code>

<code>default-auth                      (no default value)</code>

<code>host                              (no default value)</code>

<code>status                            false</code>

<code>keys                              false</code>

<code>plugin-</code><code>dir</code>                        <code>(no default value)</code>

<code>port                              0</code>

<code>show-table-</code><code>type</code>                   <code>false</code>

<code>socket                            (no default value)</code>

<code>user                              (no default value)</code>

      确实和源码编译 mysql 得到的 mysqlshow 一致,到此暂时走出了研究 mysql 客户端实现的第一步。可以稍微总结一下了:

直接使用 mysql c api 来编写程序是阻塞的还是非阻塞的? 

mysql c api 能够和 libevent 结合在一起么? 或者说哪些 api 可以?

是否需要基于 libevent 的 mysql 协议实现?

上述问题需要综合整体设计来给结论(读者自己先思考下)。

继续阅读