最初的想法是,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 协议实现?
上述问题需要综合整体设计来给结论(读者自己先思考下)。