From: https://www.felix021.com/blog/read.php?2102php
昨天@Zind同窗找到我以前的一篇blog(已經修改),裏面提到了mysql_ping和MYSQL_OPT_RECONNECT的一些事情。
之因此寫那篇blog,是由於去年寫的一些代碼遇到了「2006:MySQL server has gone away」錯誤。這個問題是由於wait_timeout這個參數的默認值是28800,也就是說,若是一個鏈接連續8個小時沒有任何請求,那麼Server端就會把它斷開。在測試環境中一個晚上沒有請求很正常……因而次日早上來的時候就發現這個錯誤了。
其實我有考慮這個問題的,真的……由於我知道php裏面有個函數叫作mysql_ping(),PHP手冊上說:「mysql_ping() 檢查到服務器的鏈接是否正常。若是斷開,則自動嘗試鏈接。本函數可用於空閒好久的腳原本檢查服務器是否關閉了鏈接,若是有必要則從新鏈接上。」
回想起來,之前真是很傻很天真。根據MySQL官方C API裏mysql_ping()的文檔:"Checks whether the connection to the server is working. If the connection has gone down and auto-reconnect is enabled an attempt to reconnect is made. ... Auto-reconnect is disabled by default. To enable it, call mysql_options() with the MYSQL_OPT_RECONNECT option",也就是說,它實際上還依賴於MYSQL_OPT_RECONNECT這個配置,而這個配置默認(自5.0.3開始)是關閉的!
雖然想起來很憤怒很蛋疼,不過看到 libmysql/client.c: mysql_init() 裏的註釋就淡定了:html
好吧,既然有問題,那就正視它。解決辦法是調用 mysql_options ,將MYSQL_OPT_RECONNECT設置爲1:mysql
可是!! 在mysql 5.0.19 以前,mysql->reconnect = 0 這一句是放在 mysql_real_connect() 裏面的!也就是說,若是你不能像處理其餘選項同樣,而是必須在mysql_real_connect()以前設置MYSQL_OPT_RECONNECT,坑爹啊!
好吧好吧,總之,關於坑的問題暫告一段落,結論就是,不論是哪一個版本,若是你想要啓用自動重連,最好都是在mysql_real_connect()以後,反正不會錯。
而後這篇的重點來了(前面彷佛太羅嗦了點):MYSQL_OPT_RECONNECT的文檔裏頭說了,這個選項是用來啓用/禁用(當發現鏈接斷開時的)自動重連,那麼,MYSQL何時會發現連接斷開呢?
這個問題可能太大了,不過不妨先去追一下,mysql_ping()作了啥。
下載源碼 http://cdn.mysql.com/Downloads/MySQL-5.1/mysql-5.1.67.tar.gz ,解壓之後ctags -R,再vim -t mysql_ping ,立刻就定位到了,彷佛太簡單了點:sql
好吧,看來關鍵在於這個simple_command了。ctrl+],原來是這樣:vim
好吧,先去追一下MYSQL,裏頭有個 const struct st_mysql_methods *methods ,再追一下 st_mysql_methods ....安全
坑爹啊!又是這種鳥代碼!蛋疼的C語言!struct只有屬性沒有方法!沒辦法,只能暴力了:服務器
果斷追到client_methods:session
也就是說simple_command最後調用了cli_advanced_command這個函數。前面的 simple_command(mysql,COM_PING,0,0,0) 至關因而調用了 cli_advanced_command(mysql, COM_PING, 0, 0, 0, 0, 0, NULL) 。
這個函數作了啥呢。。。其實也不復雜:
1. 設置默認返回值爲1 (意外出錯goto時被返回)
2. 設置sigpipe的handler(以便忽略它)
3. 若是 mysql->net.vio == 0 ,那麼調用mysql_reconnect重連,失敗的話就返回1
4. mysql沒準備好,返回1
5. 清除以前的信息(錯誤碼、緩衝區、affected_rows)等等
6. 調用net_write_command將命令發送給server,若是失敗:
6.1 檢查錯誤信息,若是是由於發送包太大,goto end
6.2 調用end_server(mysql)關閉鏈接
6.3 調用mysql_reconnect嘗試重連,若是失敗goto end
6.4 再次調用net_write_command將命令發送給server,失敗則goto end
7. 設置result = 0(發送成功)
8. 若是參數中要求檢查server的返回,則讀取一個packet進行檢查(失敗的話就result=1)
9. (end標籤)
10. 恢復sigpipe
11. 返回result
能夠看到,這裏兩次調用了mysql_reconnect,但都是有條件的:第一次是在mysql->net.vio == 0的狀況下,第二次是net_write_command失敗且不是由於包太大的狀況。vio相關的代碼看得一頭霧水,實在找不出頭緒,因而決定暴力一點:直接修改這個函數,加入一堆fprintf(stderr, ...)(具體加在哪裏就不說了,反正使勁塞就是了),而後寫了一個C代碼:ide
運行輸出:函數
根據fprintf的輸出,發如今正常狀況下,mysql->net.vio這個指針並不等於0,因此第一個mysql_reconnect不會被調用。而net_write_command也是正確執行,第二個reconnect也沒被調用。
而在執行完一個query,而後重啓mysql server再執行query (mysql_query => mysql_real_query => mysql_send_query => cli_advanced_command),就會發現,mysql->net.vio仍然不等於0,可是net_write_command失敗了,因而先調用了end_server()(這裏面會將mysql->net.vio設置爲0,不過不影響後面的流程...),而後調用了第二個reconnect,這個reconnect會調用mysql_init()以及mysql_real_query()執行一些初始化的命令,因而又回到cli_advanced_command,再一步一步回溯。。。
綜上可知,若是設置了MYSQL_OPT_RECONNECT(),那麼mysql_query()是能夠完成自動重連的。實際上,因爲cli_advanced_command會在必要狀況下調用mysql_reconnect(實際上這個函數也只在這裏被調用),所以,全部用到了cli_read_query_result的地方(或者simple_command),也均可以完成自動重連。
完結。
//混蛋,這篇純粹是爲了湊一月至少一篇這個目標啊!
--
轉載請註明出自 https://www.felix021.com/blog/read.php?2102 ,如是轉載文則註明原出處,謝謝:)
RSS訂閱地址: http://www.felix021.com/blog/feed.php 。