}
} return NGX_OK;} static ngx_int_t ngx_thread_pool_init(ngx_thread_pool_t *tp, ngx_log_t *log, ngx_pool_t *pool) { ...... for (n = 0; n < tp->threads; n++) { err = pthread_create(&tid, &attr, ngx_thread_pool_cycle, tp); ...... } ...... return NGX_OK; } static void * ngx_thread_pool_cycle(void *data) { ...... for ( ;; ) { ...... ngx_thread_cond_wait(&tp->cond, &tp->mtx, tp->log); ...... task->handler(task->ctx, tp->log); /* 执行任务处理,可以是阻塞操作 */ ..... (void) ngx_notify(ngx_thread_pool_handler); /* 让主线程得到回调 */ } } 在nginx工作进程启动时,被创建的线程也进入预备状态,这个很类似epoll_wait,但线程是在条件满足时进入wait的代码流程的。所以nginx已经将线程的外部流程做了足够的工作,让你可以只关注跑在线程里的开发。接下来了解下线程开发需要知道的结构体。每个线程池有一个任务队列,挂着你添加的任务,任务的结构体为: struct ngx_thread_task_s { ngx_thread_task_t *next; ngx_uint_t id; void *ctx; void (*handler)(void *data, ngx_log_t *log); ngx_event_t event; }; 其实非常的简单,你只需要关注两个 handler和event: handler是跑在线程里的处理,即上面的task->handler。 event是用于回调给主线程的,所以还需要注册一个event->handler,这个是nginx内部的eventfd机制处理的。 4、开发实践 以nginx-http-mysql-module为例解释下如何开发: a、添加任务 tp = mlcf->thread_pool; task = ngx_thread_task_alloc(r->pool, 0); if (task == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } task->ctx = r; task->handler = ngx_http_mysql_thread_handler; task->event.data = r; task->event.handler = ngx_http_mysql_thread_event_handler; if (ngx_thread_task_post(tp, task) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } b、处理线程任务 ctx = ngx_http_get_module_ctx(r, ngx_http_mysql_module); rc = ngx_http_mysql_run(r); if (rc == NGX_ERROR) { ctx->err = 1; } 这里需要注意的是,在线程里执行的代码不要处理任务错误,将执行结果放到ctx即可。 c、处理回调 r = ev->data; ctx = ngx_http_get_module_ctx(r, ngx_http_mysql_module); if (ctx->err) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } rc = ngx_http_mysql_output(r); ngx_http_finalize_request(r, rc); 开发非常简单吧,只需要了解简单的nginx处理,大部分业务开发可以利用第三方现成的api库。 5、想法需要更多碰撞线程池可以发挥更多作用,比如可以把连接放到线程池里。nginx的异步加lua的协程是个非常好的组合,现在有了线程池后,线程池加协程将是另一个选择。总而言之,如果在保证性能的情况下,让nginx开发变得非常简单,这是非常利好的消息。真诚欢迎更多关于这个话题的见解。
推荐阅读: