天天看點

【原創】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 協定實作?

上述問題需要綜合整體設計來給結論(讀者自己先思考下)。

繼續閱讀