mysql網路部分代碼

來源:互聯網
上載者:User

標籤:address   設定   擷取   一段   oid   oop   建立   style   bre   

今天終有點空閑(心情?)整理下mysql的網路部分代碼了。整體網路部分的代碼是非常簡單的,mysql幾乎沒有做過多的封裝,很直接的調用系統函數,一目瞭然。

總體看來,mysql主要使用了一個串連一個線程的模型,對用戶端串連的socket使用非阻塞模式。linux版本,甚至包括OSX都用的是poll函數監聽用戶端請求。中間細節的處理沒有太多複雜的操作,可見mysql這一塊沒有很特別的最佳化。

先說說Mysql啟動網路幾個關鍵函數。

Mysql啟動時,首先進入的是mysql_main這個函數,這個函數相當於main函數。在這個main中,主要做了一些列初始化工作,包括初始化環境變數,shutdown線程,最後進入無限迴圈處理用戶端串連請求,當然,無線迴圈可以被關閉操作或者異常打斷,從而退出mysqld。Main首先調用network_init()進行初始化網路socket,在最後,調用handle_connection_sockets()進入迴圈處理用戶端請求串連。簡單寫出來的虛擬碼就是如下:

int sql_main(){       network_init(); // 初始化socket       handle_connection_sockets();// 迴圈處理串連請求       return 0;}

 

 

以下是network_init()的虛擬碼,忽略了一些平台,錯誤處理的代碼,唯寫出主要邏輯:

void network_init() {       // 地址資訊,ai是一個鏈表,而a則是bind的地址資訊       struct addrinfo *ai, *a;       // set_port是確定mysql的監聽連接埠,預設下會取編譯配置的連接埠,    // 若編譯配置都沒有設定,則用3306    // 環境變數MYSQL_TCP_PORT會覆蓋以上配置       set_port();       // 調用getaddrinfo通過主機名稱獲得地址資訊,注意這裡的hint.ai_flags設定為AI_PASSIVE表示只擷取能bind的地址資訊。預設下host name是”0.0.0.0”       hint.ai_flags = AI_PASSIVE;       getaddrinfo(host name, hint, &ai);        // 調用create_socket根據地址資訊建立出ip_sock,這個ip_sock就是用了accept串連請求的。ip_sock是全域變數。       ip_sock = create_socket(ai, a);       // 設定地址可複用,這樣mysqld可以從停止中快速重新bind,而不用等待time wait狀態       setsockopt(ip_sock, reuseaddr, 1);       // 關閉只使用ipv6       setsockopt(ip_sock, ipv6only, 0);         // 以下mysql使用了一個for迴圈多次嘗試bind,這也是為了保證bind的成功率吧,每次嘗試若失敗,則sleep一段時間。       // sleep x seconds for every try       // x: 1, 3, 7, 13, 22, 35, 52, 74, ...       for(retry = 1;;retry++) {              ret = bind(ip_sock, a);              // bind成功出去繼續              if (0 == ret) {                     break;              }              // 失敗,若不是因為地址仍然在使用中的錯誤,就出去繼續,因此mysqld就會啟動失敗了              if (SOCKET_EADDRINUSE != errno){                     break;              }              // 嘗試失敗過多,mysqld啟動失敗              // 這個可以在mysql啟動時用參數--port-open-timeout=#設定啟動最多等待時間              if(wait_time > max_wait_time){                     break;              }              wait_time += retry * retry / 3 + 1;              sleep(wait_time);        }       listen(ip_sock, back_log);
    // 以下,讀出mysql.sock這個檔案,把本機sock參數載入進來,這樣mysql其實就有兩個socket來出來用戶端串連請求,一個是上面的,一個是下面這個主要給本機使用的,其代碼更簡單#ifdef UNIX_SOCK unix_sock = socket(); strmov(UNIXaddr.sun_path, mysqld_unix_port); // load the local address setsockopt(unix_sock, reuseaddr, 1); bind(unix_sock); listen(unix_sock, back_log);#endif // #ifdef UNIX_SOCK}

 

 

以上代碼中,set_port()和create_socket()是很簡單的,僅僅是簡單的調用getenv來獲得環境變數從而設定port,而create_socket()則是調用socket來產生socket通訊端,所以就不列出了。

接下來就會進入handle函數了,handle函數主要是處理串連請求,主要實現辦法是poll監聽請求的到來,然後把accept出new_sock,把new_sock交給一個建立的線程,之後的任務就是這個子線程的事了:

void handle_connection_sockets(){       int socket_count = 0;    // 是的就兩個,一個ip_sock,一個unix_sock,前者用來accept遠程用戶端,後者用來accept本機       pollfd fds[2];       fds[socket_count].fd = ip_sock;       fds[socket_count].events= POLLIN;       int ip_flags = fcntl(ip_sock, F_GETFL, 0);       socket_count++;       fds[socket_count].fd = unix_sock;       fds[socket_count].events= POLLIN;       int socket_flags=fcntl(unix_sock, F_GETFL, 0);       socket_count++;       int sock = 0, flags = 0, new_sock = 0;       // 這就是mysqld主線程的主迴圈       while(!abort_loop){              // poll監聽              int ret_val = poll(fds, socket_count, -1);              for (int i = 0; i < socket_count; ++i){                     if (fds[i].revents & POLLIN){                            sock = fds[i].fd;                            flags = fcntl(sock, F_GETFL, 0);                            break;                     }              }              // 非阻塞的socket              fcntl(sock, F_SETFL, flags | O_NONBLOCK);               // accept,這裡accept10次,直到成功為止,10次都不成功,則這個串連請求失敗              // MAX_ACCEPT_RETRY = 10              for (int retry = 0; retry == MAX_ACCEPT_RETRY; ++retry) {                     new_sock = accept(sock);                     if (no error) {                            break;                     }                     sleep(1);              }              if (new_sock == INVALID_SOCKET){                     continue;              }              // 獲得對端的地址資訊              sockaddr_storage dummy;              if (getsockname(new_sock, &dummy) < 0){                     shotdown(new_sock, SHUT_RDWR);                     close(new_sock);                     continue;              }              // 以下建立THD這個對象,THD是mysql線程對象,放了很多資訊,還沒研究              // ……       }}

 

今天就研究到這裡。

 

mysql網路部分代碼

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.