slave IO流程之一:mysql登陆过程(mysql_real_connect)
最近看了slave IO的源码,发现slave IO的写relay log貌似是单线程单连接的,这让我有点小失望。
slave IO的主函数是handle_slave_io,处理流程如下:
图1 handle_slave_io处理流程
我们这次主要要完成safe_connect以及try_to_reconnet用到的核心函数 mysql_real_connect流程的探索。
一、mysql_real_connect流程
在这之前我们需要弄明白连接mysql需要那几步操作,参考自官网的文档(http://dev.mysql.com/doc/internals/en/plain-handshake.html),据说连接时需要以下操作:
图2 mysql_real_connect操作流程
1.建立与mysql的连接
对于需要连接的建立一个监听端口,然后建立与链表中的所有服务端建立连接,并绑定到监听端口
1 if (!net->vio && 2 (!mysql->options.protocol || 3 mysql->options.protocol == MYSQL_PROTOCOL_SOCKET) && 4 (unix_socket || mysql_unix_port) && 5 (!host || !strcmp(host,LOCAL_HOST))) 6 { 7 my_socket sock= socket(AF_UNIX, SOCK_STREAM, 0); 8 DBUG_PRINT("info", ("Using socket")); 9 if (sock == SOCKET_ERROR) 10 { 11 set_mysql_extended_error(mysql, CR_SOCKET_CREATE_ERROR, 12 unknown_sqlstate, 13 ER(CR_SOCKET_CREATE_ERROR), 14 socket_errno); 15 goto error; 16 } 17 18 net->vio= vio_new(sock, VIO_TYPE_SOCKET, 19 VIO_LOCALHOST | VIO_BUFFERED_READ); 20 if (!net->vio) 21 { 22 DBUG_PRINT("error",("Unknow protocol %d ", mysql->options.protocol)); 23 set_mysql_error(mysql, CR_CONN_UNKNOW_PROTOCOL, unknown_sqlstate); 24 closesocket(sock); 25 goto error; 26 } 27 28 host= LOCAL_HOST; 29 if (!unix_socket) 30 unix_socket= mysql_unix_port; 31 host_info= (char*) ER(CR_LOCALHOST_CONNECTION); 32 DBUG_PRINT("info", ("Using UNIX sock '%s'", unix_socket)); 33 34 memset(&UNIXaddr, 0, sizeof(UNIXaddr)); 35 UNIXaddr.sun_family= AF_UNIX; 36 strmake(UNIXaddr.sun_path, unix_socket, sizeof(UNIXaddr.sun_path)-1); 37 38 if (vio_socket_connect(net->vio, (struct sockaddr *) &UNIXaddr, 39 sizeof(UNIXaddr), get_vio_connect_timeout(mysql))) 40 { 41 DBUG_PRINT("error",("Got error %d on connect to local server", 42 socket_errno)); 43 set_mysql_extended_error(mysql, CR_CONNECTION_ERROR, 44 unknown_sqlstate, 45 ER(CR_CONNECTION_ERROR), 46 unix_socket, socket_errno); 47 vio_delete(net->vio); 48 net->vio= 0; 49 goto error; 50 } 51 mysql->options.protocol=MYSQL_PROTOCOL_SOCKET; 52 }
1 for (t_res= res_lst; t_res; t_res= t_res->ai_next) 2 { 3 DBUG_PRINT("info", ("Create socket, family: %d type: %d proto: %d", 4 t_res->ai_family, t_res->ai_socktype, 5 t_res->ai_protocol)); 6 7 sock= socket(t_res->ai_family, t_res->ai_socktype, t_res->ai_protocol); 8 if (sock == SOCKET_ERROR) 9 { 10 DBUG_PRINT("info", ("Socket created was invalid")); 11 /* Try next address if there is one */ 12 saved_error= socket_errno; 13 continue; 14 } 15 16 if (client_bind_ai_lst) 17 { 18 struct addrinfo* curr_bind_ai= NULL; 19 DBUG_PRINT("info", ("Attempting to bind socket to bind address(es)")); 20 21 /* 22 We'll attempt to bind to each of the addresses returned, until 23 we find one that works. 24 If none works, we'll try the next destination host address 25 (if any) 26 */ 27 curr_bind_ai= client_bind_ai_lst; 28 29 while (curr_bind_ai != NULL) 30 { 31 /* Attempt to bind the socket to the given address */ 32 bind_result= bind(sock, 33 curr_bind_ai->ai_addr, 34 curr_bind_ai->ai_addrlen); 35 if (!bind_result) 36 break; /* Success */ 37 38 DBUG_PRINT("info", ("bind failed, attempting another bind address")); 39 /* Problem with the bind, move to next address if present */ 40 curr_bind_ai= curr_bind_ai->ai_next; 41 } 42 43 if (bind_result) 44 { 45 /* 46 Could not bind to any client-side address with this destination 47 Try the next destination address (if any) 48 */ 49 DBUG_PRINT("info", ("All bind attempts with this address failed")); 50 saved_error= socket_errno; 51 closesocket(sock); 52 continue; 53 } 54 DBUG_PRINT("info", ("Successfully bound client side of socket")); 55 } 56 57 /* Create a new Vio object to abstract the socket. */ 58 if (!net->vio) 59 { 60 if (!(net->vio= vio_new(sock, VIO_TYPE_TCPIP, flags))) 61 { 62 set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); 63 closesocket(sock); 64 freeaddrinfo(res_lst); 65 if (client_bind_ai_lst) 66 freeaddrinfo(client_bind_ai_lst); 67 goto error; 68 } 69 } 70 /* Just reinitialize if one is already allocated. */ 71 else if (vio_reset(net->vio, VIO_TYPE_TCPIP, sock, NULL, flags)) 72 { 73 set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate); 74 closesocket(sock); 75 freeaddrinfo(res_lst); 76 if (client_bind_ai_lst) 77 freeaddrinfo(client_bind_ai_lst); 78 goto error; 79 } 80 81 DBUG_PRINT("info", ("Connect socket")); 82 status= vio_socket_connect(net->vio, t_res->ai_addr, 83 (socklen_t)t_res->ai_addrlen, 84 get_vio_connect_timeout(mysql)); 85 /* 86 Here we rely on vio_socket_connect() to return success only if 87 the connect attempt was really successful. Otherwise we would 88 stop trying another address, believing we were successful. 89 */ 90 if (!status) 91 break; 92 93 /* 94 Save either the socket error status or the error code of 95 the failed vio_connection operation. It is necessary to 96 avoid having it overwritten by later operations. 97 */ 98 saved_error= socket_errno; 99 100 DBUG_PRINT("info", ("No success, try next address.")); 101 }