欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

MySQL源码探索01连接处理和登录验证以及协议类型

程序员文章站 2023-11-14 17:46:52
这篇博客是在学习MySQL Server 5.7版的源代码过程中编写的第一个学习笔记,主要内容包括: 连接处理总体过程 连接监听总体过程 登录验证 命令交互总体过程 命令的种类 命令的执行结果 支持的协议以及相关的类 连接处理总体过程 在一个线程中来处理与一个mysql客户端程序的整个会话过程中的全 ......
 
这篇博客是在学习mysql server 5.7版的源代码过程中编写的第一个学习笔记,主要内容包括:
连接处理总体过程
连接监听总体过程
登录验证
命令交互总体过程
命令的种类
命令的执行结果
支持的协议以及相关的类
 
 
 
连接处理总体过程
在一个线程中来处理与一个mysql客户端程序的整个会话过程中的全部网络数据的读写操作。这个会话结束后不会关闭这个线程,而是等待下一个mysql客户端程序的连接,整个过程反复循环,直到mysql服务器退出。
MySQL源码探索01连接处理和登录验证以及协议类型
 
  图01-01
 
源代码中的这个函数还有一些其它的代码,分析之后认为要么与这里的主题关系不大,要么是一些调试用途的代码,为了更加清晰的展示这里要描述的主题内容,做了一些删减。为了节省篇幅,对代码格式也做了一些调整。
 
当对一个mysql客户端连接的处理到达这个函数中的时候,tcp连接已经建立完毕,因此,这个函数中都是mysql命令交互处理的实质内容。
 
(1)连接的具体信息包含在channel_info中。
channel_info类是一个抽象类,具体的类中包含了通道信息,主要包括以下几个:
channel_info_local_socket:本地套接字通道信息。
channel_info_tcpip_socket:tcp套接字通道信息。
channel_info_named_pipe:命名管道通道信息。
channel_info_shared_mem:共享内存通道信息。
 
(2)init_new_thd()函数主要做以下的工作:
创建一个thd对象,后续这个连接上的数据库处理都使用这个对象。
创建连接对应的vio对象,后续这个连接上的数据收发都通过这个vio对象。
(iii)销毁channel_info对应的对象,后续不需要了。
 
 
thd这个类并不是一个os线程相关的类,而是一个专注于mysql查询密切相关处理的类。
 
MySQL源码探索01连接处理和登录验证以及协议类型
 
图01-02
 
 
thd_prepare_connection()。
主要工作是做用户登录验证。具体内容在稍后详细分析。
 
while循环。
在while()循环中使用do_command()每次接收一个命令然后执行再返回执行结果给mysql客户端程序。
 
end_connection()。
将线程与连接脱钩。
 
(6)close_connection()。
断开连接。
 
(7)release_resource()。
 释放thd中的相关资源。
 
per_thread_connection_handler::block_until_new_connection()。
阻塞当前线程,直到新的连接分配到这个线程。核心就是使用了一个条件变量,然后循环直到条件满足。条件满足后从一个list中取得一个channel_info对象,然后继续执行handle_connection()中的for(;;)循环。
 
 
MySQL源码探索01连接处理和登录验证以及协议类型
 
图01-03
 
 
这个list中的数据从哪里来呢?稍后具体介绍这个问题。
 
连接监听总体过程
 
可以看到这个per_thread_connection_handler类还有一个方法:
per_thread_connection_handler::add_connection()。
 
这个方法首先检查是否有空闲的线程可以使用,如果有则将新的channel_info存入到这个list中,然后发送信号,这样block_until_new_connection()中可以检测到条件变量的信号。
如果没有则创建一个新的线程,将新的channel_info作为新线程的函数的参数。
通过这两种方式,可以保证新的连接得到及时的处理。
 
connection_event_loop()函数直接在mysqld_main()函数中调用,即这个事件循环函数直接在主线程中执行。最终每次取得一个新连接的channel_info对象,都会调用add_connection()方法。
 
 
MySQL源码探索01连接处理和登录验证以及协议类型
图01-04
 
现在来看一下对于tcp通道的listener, 具体处理监听事件的函数如下:
channel_info* mysqld_socket_listener::listen_for_connection_event()。
这个函数仅仅通过poll()系统调用处理tcp监听套接字的accept事件,然后调用accept()系统调用实际得到一个收发数据用途的套接字,最后创建一个channel_info对象。
 
MySQL源码探索01连接处理和登录验证以及协议类型
 
图01-05
 
至此,mysql服务器处理一个新的mysql客户端程序的tcp连接的总体过程基本清楚了。
 
 
登录验证
 
前面已经知道了在mysql server与mysql客户端程序建立了连接通道后,会先进行一个登录验证的过程。这一部分开始了解mysql server的登录验证过程。这一过程主要是通过acl_authenticate()函数进行的。
 
这个函数实际上调用登录验证插件来完成相应的功能。以下语句可以查看当前使用的默认登录验证插件。
mysql> show variables like '%authen%';
+-------------------------------+-----------------------+
| variable_name                 | value                 |
+-------------------------------+-----------------------+
| default_authentication_plugin | mysql_native_password |
+-------------------------------+-----------------------+
1 row in set (0.01 sec)
 
 
登录验证的插件有以下几种,默认是mysql_native_password。
 
MySQL源码探索01连接处理和登录验证以及协议类型
 
图02-01
 
mysql server向mysql客户端程序发送一个随机的scrambe数据。这个数据是20个字节长度。这个值用于服务器校验客户端程序的登录验证插件的版本是否兼容。
每当一个客户端连接上来后,服务器发送一个随机数据给客户端,客户端使用一定的算法处理后将结果返回给服务器,服务器校验这个返回值,如果和服务器自己计算处理的结果相匹配,则认为客户端登录验证插件的版本是兼容的,否则认为不兼容。这种处理方式也是网络应用开发中的一种常见的“套路”,主要用来识别客户端版本的兼容性,同时具备一定的防范假冒客户端程序的非法攻击的作用。
 
mysql程序收到后按照一定的算法进行处理,将处理后的数据以及用户名、加密后的密码,以及主机名或ip地址发送给服务器。
以下是这个数据的内容的一个例子,其中user_name和auth_string是用户名和密码,host_or_ip是主机名。authenticated_as是服务器匹配到的用户名。
 
MySQL源码探索01连接处理和登录验证以及协议类型
 
图02-02
服务器判断数据的有效性之后,进行必要的检查。
检查用户是否需要ssl模式。
是否处于skip-grant模式,即忽略授权检查。
用户账户是否已经处于锁定状态。
用户密码是否已经过期。
全局:同一个用户登录的连接是否已经太多了。
全局:是否达到每小时连接数的限制。
全局:是否达到最大连接数的限制。
用户级别:同一个用户登录的连接是否已经太多了。
用户级别:是否达到每小时连接数的限制。
用户级别:是否达到最大连接数的限制。
 
服务器将检查的结果发送给客户端程序。
通过以下函数发送检查结果。
void thd::send_statement_status()
登录成功发送da::ok,失败则发送其它值。
 
 
 
命令交互总体过程
 
前面已经了解了mysql服务器连接处理的总体过程,其中跟命令处理有关的是do_command()这个函数。这个函数每次执行都处理一个sql命令。
 
 
MySQL源码探索01连接处理和登录验证以及协议类型
图03-01
 
get_command()的工作是通过网络读取一个命令到com_data中,dispatch_command()的工作是负责执行一个命令,并将执行结果反馈给客户端程序。
com_data相关的具体结构暂时不关注。
 
dispatch_command()函数的主体结构如下所示:
 
 
MySQL源码探索01连接处理和登录验证以及协议类型
图03-02
 
 
 
 
 
命令的种类
总共有以下的各种命令,最常用的sql中的增删改查操作对应于com_query命令,而com_connect命令只有在登录验证时有用到。这些命令基本上都是名称就已经充分的说明了它的功能,所以就不再赘述了。
 
 
MySQL源码探索01连接处理和登录验证以及协议类型
图03-03
 
命令的执行结果
 
总共有5种可能的执行结果:执行成功就是da_ok,其它都是出错的状态。
 
 
 
MySQL源码探索01连接处理和登录验证以及协议类型
图03-04
 
 
支持的协议以及相关的类
mysql中定义了以下几种协议类型:
  protocol_text:文本协议,用于mysql server v4.0或更早的版本。
  protocol_binary:二进制协议。
  protocol_local:本地协议。
mysql中有提到所谓的经典的协议(classic),指的是文本协议或者二进制协议。
 
mysql中处理这些协议相关问题的类的基类是protocol这个类,定义了收发各种数据的接口函数。粒度比较大的函数有get_command()函数,粒度比较细的有发送一个整数这样的函数,比如store_long()等。
protocol_classic这个类派生于protocol类,处理经典的协议类型。protocol_text派生于protocol_classic,而protocol_binary又派生于protocol_text类。
 
 
 
this is more complex than it looks.
 
这句话出现在mysql server的源代码的native_password_authenticate()这个函数中,正好作为这篇博客的一个结束语。