Oracle MySQL只公开了前两种源码,企业版中线程池以插件的形式实现提供,Mariadb提供了三种线程调度方法:
one_thread_scheduler_functions:
由main函数处理连接,连接断开后,等待处理其他连接
one_thread_per_connection_scheduler_functions:
为每个连接创建一个处理线程,连接断开后,处理线程被释放
tp_scheduler_functions:
线程池处理连接,由一定数量的work thread共同处理接入的连接
线程调度是由一组函数接口规定的,server层通过回调这些函数来处理连接
- struct scheduler_functions
- {
- uint max_threads, *connection_count;
- ulong *max_connections;
- bool (*init)(void);
- bool (*init_new_connection_thread)(void);
- void (*add_connection)(THD *thd);
- void (*thd_wait_begin)(THD *thd, int wait_type);
- void (*thd_wait_end)(THD *thd);
- void (*post_kill_notification)(THD *thd);
- bool (*end_thread)(THD *thd, bool cache_thread);
- void (*end)(void);
- };
三种具体的线程调度方案,函数指针初始化如下:
由main函数处理连接:
- static scheduler_functions one_thread_scheduler_functions=
- {
- 1, // max_threads
- &connection_count,
- &max_connections,
- NULL, // init
- init_new_connection_handler_thread, // init_new_connection_thread
- handle_connection_in_main_thread, // add_connection
- NULL, // add_connection
- NULL, // thd_wait_begin
- NULL, // thd_wait_end
- NULL, // post_kill_notification
- no_threads_end, // end_thread
- NULL, // end
- };
单线程调度通过handle_connection_in_main_thread调用do_handle_one_connection来做,它会首先回调init_new_connection_handler_thread做一些初始化工作,之后进入一个for(;;)循环,之后就看到了熟悉的do_command函数了,通过它就可以进入mysql执行查询了,其end函数指针被初始化no_threads_end,做的事情比较简单,清理了连接对应的thd,之后继续接受下一个连接
一个连接一个处理线程:
- static scheduler_functions one_thread_per_connection_scheduler_functions=
- {
- 0, // max_threads
- &connection_count,
- &max_connections,
- NULL, // init
- init_new_connection_handler_thread, // init_new_connection_thread
- create_thread_to_handle_connection, // add_connection
- NULL, // thd_wait_begin
- NULL, // thd_wait_end
- NULL, // post_kill_notification
- one_thread_per_connection_end, // end_thread
- NULL, // end
- };
每来一个连接都会在create_thread_to_handle_connection中获得线程,它首先会在这thread_cache中找,找到了就唤醒work thread,找不到才创建线程,对应的线程函数为handle_one_connection,与单线程调度类似,最终通过调用do_handle_one_connection函数来进入do_command的for(;;)循环,与单线程调度不同的是,其end函数指针为one_thread_per_connection_end,连接退出后做的事情会多一些,如果put_in_cache被设置,work thread会被cache起来,否则会通过my_thread_end清理线程资源
线程池处理连接:(将在下篇博客中介绍)
- static scheduler_functions tp_scheduler_functions=
- {
- 0, // max_threads
- NULL,
- NULL,
- tp_init, // init
- NULL, // init_new_connection_thread
- tp_add_connection, // add_connection
- tp_wait_begin, // thd_wait_begin
- tp_wait_end, // thd_wait_end
- tp_post_kill_notification, // post_kill_notification
- NULL, // end_thread
- tp_end // end
- };
线程回调函数接口在server层被调用的地方:
main线程中,读取配置信息,决定具体采用的thread scheduler,接着初始化网络,调用init接口完成线程调度初始化工作,之后server接受client连接,通过调用add_connection接口处理请求
- mysqld_main
- ...
- |->init_common_variables
- |->get_options
- |->
- if (thread_handling <= SCHEDULER_ONE_THREAD_PER_CONNECTION)
- one_thread_per_connection_scheduler(thread_scheduler);
- else if (thread_handling == SCHEDULER_NO_THREADS)
- one_thread_scheduler(thread_scheduler);
- else
- pool_of_threads_scheduler(thread_scheduler)
- ...
- |->network_init(void)
- |->MYSQL_CALLBACK_ELSE(thread_scheduler, init, (), 0)
- ...
- |->handle_connections_sockets
- |->create_new_thread // 会对连接数进行判断
- |->MYSQL_CALLBACK(thread_scheduler, add_connection, (thd))
- ...
server在退出时,会回调post_kill_notification接口通知线程
- mysqld_exit
- |->
- I_List_iterator<THD> it(threads);
- while (it++)
- MYSQL_CALLBACK(thread_scheduler, post_kill_notification, it)
线程等待回调:
- thd_wait_begin
- |->MYSQL_CALLBACK(thd->scheduler, thd_wait_begin, (thd, wait_type))
- thd_wait_end
- |->MYSQL_CALLBACK(thd->scheduler, thd_wait_end, (thd))
连接中断,退出,错误处理
- |->MYSQL_CALLBACK_ELSE(thd->scheduler, end_thread, (thd, 1), 0)
由于线程调度的接口的定义比较清晰,线程调度方案的具有很好的扩展性,而mariadb的线程池处理连接的实现就是通过实现了scheduler_functions线程调度接口来扩展的,代码写得比较优雅,接下来几天将抽空总结一下~
阅读(5237) | 评论(0) | 转发(0) |