【Linux网络编程】Nginx -- 线程池
【1】Nginx 线程池概述
Nginx 线程池相关代码路径如下,src\core\ngx_thread_pool.h/ngx_thread_pool.c
编译时使用如下选项可以启用线程池功能
--with-threads
--with-file-aio
启用线程池功能,让请求排队等待处理,并且可以充分利用 CPU 提高处理效率,开启线程池需要 AIO 的支持,启用异步文件 IO (AIO) 一般用于大文件传输的场景;
Nginx 线程池源码分析
线程池处理图示
【2】ngx_thread_pool_module -- Nginx 线程池模块定义
【2.1】ngx_thread_pool_module
该模块存储于全局变量 ngx_modules 中,ngx_modules 声明与定义如下
// 模块的名字数组,由 make 生成在 objs/ngx_modules.c
extern char *ngx_module_names[];
// 文件路径,objs/ngx_modules.c
extern ngx_module_t ngx_thread_pool_module;
ngx_module_t *ngx_modules[] = {
...
&ngx_thread_pool_module,
...
}
char *ngx_module_names[] = {
...
"ngx_thread_pool_module",
...
}
ngx_thread_pool_module 模块在 src\core\ngx_cycle.c 文件的 ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle) 函数中遍历获取;
ngx_module_t ngx_thread_pool_module = {
NGX_MODULE_V1,
&ngx_thread_pool_module_ctx, /* module context */
ngx_thread_pool_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
// ngx_single_process_cycle/ngx_worker_process_cycle里调用
// 进程开始时初始化,创建线程池
ngx_thread_pool_init_worker, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
// 进程结束时被调用,清理线程池
// 调用ngx_thread_pool_destroy逐个销毁线程池
ngx_thread_pool_exit_worker, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
- ngx_thread_pool_init_worker 调用位置,关键代码如下
static void
ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)
{
...
// 调用所有模块的 init_process,模块进程初始化 hook
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->init_process) {
if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {
/* fatal */
exit(2);
}
}
}
...
}
ngx_thread_pool_init_worker 函数分析
// 进程开始时初始化,创建线程池
// 1. 获取线程池配置项
// 2. 初始化线程池的任务队列
// 3. 调用 ngx_thread_pool_init 函数初始化线程池
static ngx_int_t ngx_thread_pool_init_worker(ngx_cycle_t *cycle);
- ngx_thread_pool_exit_worker 调用位置,关键代码如下
static void
ngx_worker_process_exit(ngx_cycle_t *cycle)
{
...
// 调用所有模块的 exit_process,进程结束 hook
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->exit_process) {
cycle->modules[i]->exit_process(cycle);
}
}
...
}
ngx_thread_pool_exit_worker 函数分析
// 进程结束时被调用,清理线程池
// 1. 判断进程类型,确保进程是 worker/single 进程
// 2. 获取线程池模块的配置项
// 3. 调用 ngx_thread_pool_destroy 逐个销毁线程池
static void ngx_thread_pool_exit_worker(ngx_cycle_t *cycle);
【2.2】ngx_thread_pool_module_ctx
// 线程池模块属于core模块,只有一个指令,配置有名的线程池
static ngx_core_module_t ngx_thread_pool_module_ctx = {
ngx_string("thread_pool"),
// 创建线程池模块的配置,里面是个数组,元素为ngx_thread_pool_t
ngx_thread_pool_create_conf,
// 检查配置的线程池,必须设置线程数量
ngx_thread_pool_init_conf
};
- ngx_thread_pool_create_conf 调用位置,关键代码如下
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle)
{
...
ngx_core_module_t *module;
// 初始化 core 模块
for (i = 0; cycle->modules[i]; i++) {
// 检查 type,只处理 core 模块,数量很少
if (cycle->modules[i]->type != NGX_CORE_MODULE) {
continue;
}
//获取 core 模块的函数表
module = cycle->modules[i]->ctx;
//*******************************************************
// 5. 遍历所有 core 模块,并调用该模块的 create_conf() 函数
//*******************************************************
// 创建 core 模块的配置结构体
// 有的 core 模块可能没有这个函数,所以做一个空指针检查
if (module->create_conf) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
// 存储到 cycle 的配置数组里,用的是 index,不是 ctx_index
cycle->conf_ctx[cycle->modules[i]->index] = rv;
}
}
...
}
ngx_thread_pool_create_conf 函数分析
// 创建线程池模块的配置,其中包含一个数组,数组元素为 ngx_thread_pool_t
static void *ngx_thread_pool_create_conf(ngx_cycle_t *cycle);
- ngx_thread_pool_init_conf 调用位置,关键代码如下
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle)
{
...
ngx_core_module_t *module;
//*******************************************************
// 7. 遍历所有 core 模块,并调用 core 模块的 init_conf() 函数
//*******************************************************
// 其他类型的模块都已经配置好了,最后对 core 模块配置初始化
// 如果有的参数没有明确配置,这里就调用 init_conf 设置默认值
for (i = 0; cycle->modules[i]; i++) {
// 检查 type,只处理 core 模块,数量很少
if (cycle->modules[i]->type != NGX_CORE_MODULE) {
continue;
}
// 获取 core 模块的函数表
module = cycle->modules[i]->ctx;
// 调用 core 模块的初始化函数
// 有的 core 模块可能没有这个函数,所以做一个空指针检查
if (module->init_conf) {
if (module->init_conf(cycle,
cycle->conf_ctx[cycle->modules[i]->index])
== NGX_CONF_ERROR)
{
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
}
}
...
}
ngx_thread_pool_init_conf 函数分析
// 检查线程池的配置参数,在未配置的情况下给出默认的配置参数
// threads : 32,max_queue : 65536
static char *ngx_thread_pool_init_conf(ngx_cycle_t *cycle, void *conf);
【2.3】ngx_thread_pool_commands
// 线程池模块属于core模块,只有一个指令,配置有名的线程池
// 解析thread_pool指令,设置线程数和队列数(默认65535)
static ngx_command_t ngx_thread_pool_commands[] = {
{ ngx_string("thread_pool"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE23,
ngx_thread_pool,
0,
0,
NULL },
ngx_null_command
};
- ngx_thread_pool 调用位置,关键代码如下
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle)
{
...
ngx_conf_t conf;
ngx_core_module_t *module;
//*******************************************************
// 6. 配置文件解析
//*******************************************************
// 递归执行解析动作,各个模块允许的指令配置参数
// 先解析-g传递的命令行参数
if (ngx_conf_param(&conf) != NGX_CONF_OK) {
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
// 递归执行解析动作,各个模块允许的指令配置参数
// 里面有http的post configration指针
// 再解析配置文件
if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
...
}
文件路径,src\core\ngx_conf_file.c
char *
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
{
...
rc = ngx_conf_handler(cf, rc);
...
}
文件路径,src\core\ngx_conf_file
static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{
...
// 遍历所有模块
for (i = 0; cf->cycle->modules[i]; i++) {
// 取模块的指令数组
cmd = cf->cycle->modules[i]->commands;
...
// 在此处调用设置配置参数
rv = cmd->set(cf, cmd, conf);
...
...
}
ngx_thread_pool 函数分析
// 解析 thread_pool 指令,设置线程数和队列数(默认 65535)
// 保证配置中至少包含一个默认的配置项
static char *ngx_thread_pool(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
【3】Nginx 线程池相关结构体
【3.1】ngx_thread_task_s/ngx_thread_task_t 结构体
// 线程池任务结构体
struct ngx_thread_task_s {
// 链表指针,多个 task 形成一个链表
ngx_thread_task_t *next;
// task 的 id,由全局计数器 ngx_thread_pool_task_id 生成
// task->id = ngx_thread_pool_task_id++;
// 在线程运行时用户的 handler 看不到
ngx_uint_t id;
// 用户使用的数据,也就是 handler 的 data 参数
// 用这个参数传递给线程要处理的各种数据
// 比较灵活的方式是传递一个指针,而不是真正的数据结构内容
// 由 ngx_thread_task_alloc 分配内存空间并赋值
void *ctx;
// 由线程里的线程执行的函数
// 执行用户定义的操作,通常是阻塞的
// 参数 data 就是上面的 ctx
// handler 不能直接看到 task,但可以在 ctx 里存储 task 指针
void (*handler)(void *data, ngx_log_t *log);
// 任务关联的事件对象
// event.active 表示任务是否已经放入任务队列
// 这里的 event 并不关联任何 socket 读写或定时器对象
// 仅用到它的 handler/data 成员,当线程完成任务时回调
//
// event->data 要存储足够的信息,才能够完成请求
// 可以使用 r->ctx,里面存储请求 r、连接 c 等
ngx_event_t event;
};
【3.2】ngx_thread_pool_queue_t 结构体
// 线程池使用的任务队列
typedef struct {
ngx_thread_task_t *first;
ngx_thread_task_t **last;
} ngx_thread_pool_queue_t;
【3.3】ngx_thread_pool_s/ngx_thread_pool_t 结构体
// 描述一个线程池,与 thread_pool 指令对应
// 此结构体的实际定义在 c 文件里,外部不可见,深度定制则不方便
// 存储在 ngx_thread_pool_conf_t 里的数组里
// 核心成员是 queue,存储待处理的任务
struct ngx_thread_pool_s {
// 互斥量
// 锁定互斥量,防止多线程操作的竞态
// 锁定操作 waiting/queue/ngx_thread_pool_task_id
ngx_thread_mutex_t mtx;
// 线程池里的待处理任务队列
// ngx_thread_task_post 把任务放入线程池
// ngx_thread_pool_cycle 消费任务
ngx_thread_pool_queue_t queue;
// 等待的任务数
ngx_int_t waiting;
// 文件 src\os\unix\ngx_thread.h 中
// typedef pthread_cond_t ngx_thread_cond_t;
// 条件变量,用于等待任务队列 queue
ngx_thread_cond_t cond;
// 日志对象,多线程操作也是安全的
ngx_log_t *log;
// 线程池的名字
ngx_str_t name;
// 线程的数量,默认为 32 个线程
ngx_uint_t threads;
// 任务等待队列,默认是 65535
ngx_int_t max_queue;
// 定义线程池的配置文件
u_char *file;
// 定义线程池指令的行号
ngx_uint_t line;
};
【3.4】ngx_thread_pool_conf_t 结构体
// 线程池模块的配置,其中是个数组,元素为 ngx_thread_pool_t/(ngx_thread_pool_s)
// 由 ngx_thread_pool_add 在解析指令的 ngx_thread_pool 函数时添加
// 在 ngx_thread_pool_create_conf 函数中创建
typedef struct {
ngx_array_t pools;
} ngx_thread_pool_conf_t;
【4】Nginx 线程池相关函数
【4.1】ngx_thread_pool_init 函数分析
// 使用 ngx_thread_pool_t 结构体初始化线程池
// 在 init_worker 时被调用
// 创建互斥量、条件变量,根据配置的线程数量,创建线程
// 线程的执行函数是 ngx_thread_pool_cycle,参数是线程池结构体
static ngx_int_t
ngx_thread_pool_init(ngx_thread_pool_t *tp, ngx_log_t *log, ngx_pool_t *pool)
{
int err;
pthread_t tid;
ngx_uint_t n;
pthread_attr_t attr;
// 要求必须有事件通知函数 ngx_notify
// 否则多线程无法工作
// 调用系统函数 eventfd,创建一个可以用于通知的描述符
//
// ngx_notify :
// 文件路径,src\event\ngx_event.h
// #define ngx_notify ngx_event_actions.notify
// ngx_event_actions 为全局变量
// 文件路径,src\event\ngx_event.h
// extern ngx_event_actions_t ngx_event_actions;
if (ngx_notify == NULL) {
ngx_log_error(NGX_LOG_ALERT, log, 0,
"the configured event method cannot be used with thread pools");
return NGX_ERROR;
}
// 初始化线程池任务队列,first/last都是空
ngx_thread_pool_queue_init(&tp->queue);
// 系统调用创建互斥量
if (ngx_thread_mutex_create(&tp->mtx, log) != NGX_OK) {
return NGX_ERROR;
}
// 系统调用创建条件变量
if (ngx_thread_cond_create(&tp->cond, log) != NGX_OK) {
(void) ngx_thread_mutex_destroy(&tp->mtx, log);
return NGX_ERROR;
}
// 线程池使用的log由外部传入
tp->log = log;
// 系统调用,初始化一个线程对象的属性
err = pthread_attr_init(&attr);
if (err) {
ngx_log_error(NGX_LOG_ALERT, log, err,
"pthread_attr_init() failed");
return NGX_ERROR;
}
// 线程 detach
// 设置线程为分离状态,分离线程无须被其他线程等待,自己运行结束线程便终了,立马释放资源
err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (err) {
ngx_log_error(NGX_LOG_ALERT, log, err,
"pthread_attr_setdetachstate() failed");
return NGX_ERROR;
}
#if 0
err = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
if (err) {
ngx_log_error(NGX_LOG_ALERT, log, err,
"pthread_attr_setstacksize() failed");
return NGX_ERROR;
}
#endif
// 根据配置的线程数量,创建线程
for (n = 0; n < tp->threads; n++) {
// 线程的执行函数是 ngx_thread_pool_cycle,参数是线程池结构体
// 线程创建后立即 detach
err = pthread_create(&tid, &attr, ngx_thread_pool_cycle, tp);
if (err) {
ngx_log_error(NGX_LOG_ALERT, log, err,
"pthread_create() failed");
return NGX_ERROR;
}
}
// 销毁线程属性对象
(void) pthread_attr_destroy(&attr);
return NGX_OK;
}
【4.2】ngx_thread_pool_cycle 函数分析
// 线程池里每个线程执行的函数,无限循环
// 参数是线程池结构体
// 从待处理任务队列里获取任务,然后执行 task->handler(task->ctx)
// 处理完的任务加入完成队列,触发 ngx_thread_pool_handler 函数处理
static void *
ngx_thread_pool_cycle(void *data)
{
// 参数是线程池结构体
ngx_thread_pool_t *tp = data;
int err;
sigset_t set;
ngx_thread_task_t *task;
#if 0
ngx_time_update();
#endif
ngx_log_debug1(NGX_LOG_DEBUG_CORE, tp->log, 0,
"thread in pool \"%V\" started", &tp->name);
// 线程的运行屏蔽几个信号
sigfillset(&set);
sigdelset(&set, SIGILL);
sigdelset(&set, SIGFPE);
sigdelset(&set, SIGSEGV);
sigdelset(&set, SIGBUS);
// 原型
// int pthread_sigmask (int how,const sigset_t *set,sigset_t *oset)
// 参数
// SIG_BLOCK : 结果集是当前集合参数集的并集;
// SIG_UNBLOCK : 结果集是当前集合参数集的差集;
// SIG_SETMASK : 结果集是由参数集指向的集;
//
// 只在主线程中处理信号
err = pthread_sigmask(SIG_BLOCK, &set, NULL);
if (err) {
ngx_log_error(NGX_LOG_ALERT, tp->log, err, "pthread_sigmask() failed");
return NULL;
}
// 无限循环
// 从待处理任务队列里获取任务,然后执行 task->handler(task->ctx)
for ( ;; ) {
// 锁定互斥量,防止多线程操作的竞态
if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) {
return NULL;
}
/* the number may become negative */
// 即将处理一个任务,计数器减 1
tp->waiting--;
// 如果任务队列是空,那么使用条件变量等待
while (tp->queue.first == NULL) {
if (ngx_thread_cond_wait(&tp->cond, &tp->mtx, tp->log)
!= NGX_OK)
{
(void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
return NULL;
}
}
// 此时队列里有待处理的 task
// 取出一个 task
task = tp->queue.first;
tp->queue.first = task->next;
// 如果此时队列已经空,调整指针
if (tp->queue.first == NULL) {
tp->queue.last = &tp->queue.first;
}
// 操作完 waiting、queue 后解锁,其他线程可以获取 task 处理
if (ngx_thread_mutex_unlock(&tp->mtx, tp->log) != NGX_OK) {
return NULL;
}
#if 0
ngx_time_update();
#endif
ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0,
"run task #%ui in thread pool \"%V\"",
task->id, &tp->name);
// 调用任务的 handler,传递 ctx,执行用户定义的操作,通常是阻塞的
task->handler(task->ctx, tp->log);
ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0,
"complete task #%ui in thread pool \"%V\"",
task->id, &tp->name);
task->next = NULL;
// 自旋锁保护完成队列
ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048);
// 处理完的任务加入完成队列
*ngx_thread_pool_done.last = task;
ngx_thread_pool_done.last = &task->next;
// 1.10 新增
// 确保内存操作按照正确的顺序工作的非阻塞的同步
// 迫使处理器来完成位于 barrier 前面的任何加载和存储操作
// 才允许它执行位于 barrier 之后的加载和存储操作
ngx_memory_barrier();
// 自旋锁解锁
ngx_unlock(&ngx_thread_pool_done_lock);
// 重要,使用 event 模块的通知函数
// 让主线程(nginx)的 epoll 触发事件,调用 ngx_thread_pool_handler
// 分发处理线程完成的任务
//
// 调用系统函数 eventfd,创建一个可以用于通知的描述符,用于实现 notify
(void) ngx_notify(ngx_thread_pool_handler);
} // 无限循环,回到开头再取下一个 task
}
【4.3】ngx_thread_pool_handler 函数分析
// 处理 ngx_thread_pool_done 队列中的已完成任务
// 调用 event->handler,即异步事件完成后的回调函数
static void
ngx_thread_pool_handler(ngx_event_t *ev)
{
ngx_event_t *event;
ngx_thread_task_t *task;
ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, "thread pool handler");
// 自旋锁保护完成队列
ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048);
// 取出队列里的 task,task->next 里有很多已经完成的任务
task = ngx_thread_pool_done.first;
// 把队列直接置空
// 即 ngx_thread_pool_queue_init
ngx_thread_pool_done.first = NULL;
ngx_thread_pool_done.last = &ngx_thread_pool_done.first;
// 1.10 新增
// 确保内存操作按照正确的顺序工作的非阻塞的同步
// 迫使处理器来完成位于 barrier 前面的任何加载和存储操作
// 才允许它执行位于 barrier 之后的加载和存储操作
ngx_memory_barrier();
// 自旋锁解锁
ngx_unlock(&ngx_thread_pool_done_lock);
// 遍历所有已经完成的任务
while (task) {
ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,
"run completion handler for task #%ui", task->id);
// 取 task 里的事件对象
event = &task->event;
// task 指针移动到下一个节点
task = task->next;
// 线程异步事件已经完成
event->complete = 1;
// 事件已经处理完
event->active = 0;
// 调用 handler,即异步事件完成后的回调函数
event->handler(event);
}
}
【4.4】ngx_thread_pool_destroy 函数分析
// 销毁线程池
// 使用一个要求线程结束的 task,发给池里所有的线程
// 最后销毁条件变量和互斥量
static void
ngx_thread_pool_destroy(ngx_thread_pool_t *tp)
{
ngx_uint_t n;
ngx_thread_task_t task;
// lock 是一个简单的标志量,作为任务的 ctx 传递
volatile ngx_uint_t lock;
// 创建要求线程结束的 task
ngx_memzero(&task, sizeof(ngx_thread_task_t));
// 要求线程结束的任务,调用 pthread_exit
task.handler = ngx_thread_pool_exit_handler;
// lock 是一个简单的标志量,作为任务的 ctx 传递
task.ctx = (void *) &lock;
// 发送 tp->threads 个 task,逐个结束所有的线程
for (n = 0; n < tp->threads; n++) {
// 线程退出后将会被设置为 0
lock = 1;
// 把任务加入到线程池的队列
if (ngx_thread_task_post(tp, &task) != NGX_OK) {
return;
}
// 等待 task 被某个线程处理,从而结束一个线程
while (lock) {
// ngx_process.h:#define ngx_sched_yield() sched_yield()
// 避免占用 cpu,让出主线程执行权,其他线程有机会执行
ngx_sched_yield();
}
// event.active 表示任务是否已经放入任务队列
task.event.active = 0;
}
// 销毁条件变量
(void) ngx_thread_cond_destroy(&tp->cond, tp->log);
// 销毁互斥量
(void) ngx_thread_mutex_destroy(&tp->mtx, tp->log);
}
【4.5】ngx_thread_pool_exit_handler 函数分析
// 要求线程结束的任务,调用 pthread_exit
// 把任务需要处理的 data 置为 0,表示线程结束
static void
ngx_thread_pool_exit_handler(void *data, ngx_log_t *log)
{
ngx_uint_t *lock = data;
*lock = 0;
pthread_exit(0);
}
【4.6】ngx_thread_task_post 函数分析
// 把任务放入线程池,由线程执行
// 锁定互斥量,防止多线程操作的竞态
// 如果等待处理的任务数大于设置的最大队列数,那么添加任务失败
// 操作完 waiting、queue、ngx_thread_pool_task_id 后解锁
ngx_int_t
ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task)
{
// event.active 表示任务是否已经放入任务队列
// 如果 event.active==1 则 ngx_thread_task_post 失败
// 避免任务被重复加入到任务队列中
if (task->event.active) {
ngx_log_error(NGX_LOG_ALERT, tp->log, 0,
"task #%ui already active", task->id);
return NGX_ERROR;
}
// 锁定互斥量,防止多线程操作的竞态
if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) {
return NGX_ERROR;
}
// 如果等待处理的任务数大于设置的最大队列数
// 那么添加任务失败
if (tp->waiting >= tp->max_queue) {
(void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
ngx_log_error(NGX_LOG_ERR, tp->log, 0,
"thread pool \"%V\" queue overflow: %i tasks waiting",
&tp->name, tp->waiting);
return NGX_ERROR;
}
// event.active 表示任务是否已经放入任务队列
// 如果 event.active == 1 则任务已经添加到任务队列中
task->event.active = 1;
// task id 增加
// 全局计数器, 生成 task 的 id
task->id = ngx_thread_pool_task_id++;
task->next = NULL;
// 条件变量,发送信号
// 在 ngx_thread_pool_cycle 里解除对队列的等待
if (ngx_thread_cond_signal(&tp->cond, tp->log) != NGX_OK) {
(void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
return NGX_ERROR;
}
// 把任务加入待处理队列
*tp->queue.last = task;
tp->queue.last = &task->next;
// 等待处理的任务数增加
tp->waiting++;
// 操作完waiting、queue、ngx_thread_pool_task_id后解锁
(void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0,
"task #%ui added to thread pool \"%V\"",
task->id, &tp->name);
return NGX_OK;
}
【4.7】ngx_thread_task_alloc 函数分析
// 创建一个线程任务结构体
// 参数 size 是用户数据 ctx 的大小,位于 task 之后
// 因为 C 的内存布局是平坦的,所以使用这种 hack 的方法来扩展 task 结构体
ngx_thread_task_t *
ngx_thread_task_alloc(ngx_pool_t *pool, size_t size)
{
ngx_thread_task_t *task;
// 注意,多分配了size个字节,即用户要求的ctx大小
task = ngx_pcalloc(pool, sizeof(ngx_thread_task_t) + size);
if (task == NULL) {
return NULL;
}
// 设置 task 的 ctx 指针,即在 task 地址之后的位置
task->ctx = task + 1;
return task;
}
【5】Nginx 使用实例简析
文件路径,src\http\ngx_http_file_cache.c
static ssize_t
ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
{
...
#if (NGX_THREADS)
if (clcf->aio == NGX_HTTP_AIO_THREADS) {
c->file.thread_task = c->thread_task;
// 指定处理句柄,
// ngx_http_cache_thread_handler 中将线程池任务放入线程池的任务队列中
c->file.thread_handler = ngx_http_cache_thread_handler;
c->file.thread_ctx = r;
// 此处使用线程池的方式异步读取文件数据
n = ngx_thread_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);
c->thread_task = c->file.thread_task;
// 判断是否读取完毕
c->reading = (n == NGX_AGAIN);
return n;
}
#endif
...
}
文件路径,src\http\ngx_http_file_cache.c
static ngx_int_t
ngx_http_cache_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
{
...
ngx_thread_pool_t *tp;
...
// 设置线程池任务并加入线程池任务队列中
task->event.data = r;
// 事件回调函数,当线程池任务处理完毕回调 ngx_http_cache_thread_event_handler 函数
// 在 ngx_http_cache_thread_event_handler 中发送请求
task->event.handler = ngx_http_cache_thread_event_handler;
// 将线程池任务添加到线程池的任务队列中
if (ngx_thread_task_post(tp, task) != NGX_OK) {
return NGX_ERROR;
}
r->main->blocked++;
r->aio = 1;
...
}
文件路径,src\os\unix\ngx_files.c
ssize_t
ngx_thread_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,
ngx_pool_t *pool)
{
...
// 获取或新建线程池任务
task = file->thread_task;
// 如果没有task则创建
if (task == NULL) {
task = ngx_thread_task_alloc(pool, sizeof(ngx_thread_file_ctx_t));
if (task == NULL) {
return NGX_ERROR;
}
file->thread_task = task;
}
...
// 指定线程池任务的处理句柄
//
// ngx_thread_read_handler 函数中
// 调用 pread 函数以原子定位读取数据
task->handler = ngx_thread_read_handler;
// 投递到线程池里执行
// 任务完成后回调 event 里的 handler
//
// 本例中 file->thread_handler 为 ngx_http_cache_thread_handler
if (file->thread_handler(task, file) != NGX_OK) {
return NGX_ERROR;
}
...
}
参考致谢
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。
【1】深入理解 Nginx 模块开发与架构解析
【2】理解 Nginx 源码
【3】NGINX引入线程池 性能提升9倍
【4】Nginx 的线程池与性能剖析