ngx_http_init_connection()
负责建立 http 链接的是 ngx_http_init_connection(), 我们先来看一下谁会调用这个函数。
ngx_http_optimize_servers() 是一个负责合并配置项的函数,在有关 http 配置项解析的博客中曾经提到过,它会调用 ngx_http_add_listening()。
继续看 ngx_http_add_listening(), 看到 ls->handler = ngx_http_init_connection;
即 ngx_http_init_connection() 作为一个回调函数,被设置在了 listening 结构体的 handler 中。
那么它是从哪儿被调用的呢? 最简单的方法就是打印一下backtrace
#0 ngx_http_init_connection (c=0x7ffff7fa90f8)
#1 ngx_event_accept (ev=0x73c3a0)
#2 ngx_epoll_process_events (cycle, timer, flags)
#3 ngx_process_events_and_timers (cycle=cycle@entry=0x716860)
#4 ngx_single_process_cycle (cycle=cycle@entry=0x716860)
#5 main (argc=<optimized out>, argv=<optimized out>)
原来是在 epoll 中由事件触发的,正所谓事件驱动。现在知道了它的调用者,那么继续看 init connection 具体做了哪些事。
我们先忽略对 ipv6、ssl 的处理,直接看最简单的情况,ssl 相关后续博客再分析。精简后的代码如下:
void ngx_http_init_connection() {
c->data = hc; // data 是专门存数据的地方,之后使用
// 拿到以前提到很多次的 ngx_http_conf_ctx_t
hc->conf_ctx = hc->addr_conf->default_server->ctx;
// 连接的读事件,rev 是 ngx_event_t 类型
rev = c->read;
// 处理读事件,读取请求头
// 设置了读事件的 handler,可读时就会调用 handler
rev->handler = ngx_http_wait_request_handler;
// 前面把读事件加入epoll,当socket有数据可读时就调用
// ngx_http_wait_request_handler
// 同时因为事件也加入了定时器,超时时也会调用 handler
ngx_handle_read_event(rev, 0);
}
ngx_http_wait_request_handler()
前面我们把 read 事件加入了 epoll,当 socket 有数据可读是就会调用本函数。又因为是事件触发,可能会被多次调用,即重入。
本函数的主要做的工作非常简单:把 socket 上能读的数据读出来,然后创建一个 http request 结构体,最后调用其他函数来解析 http 请求头。
void ngx_http_wait_request_handler() {
// 从事件的data获得连接对象
c = rev->data;
// 获取配置数组。回忆前面在 init_connection里设置
hc = c->data;
cscf = ngx_http_get_module_srv_conf(hc->conf_ctx,
ngx_http_core_module);
// 这里的 recv 是相当于nginx 封装的 accept() 函数
// 具体可参考 ngx_event_accept()
n = c->recv(c, b->last, size);
// 创建ngx_http_request_t
c->data = ngx_http_create_request(c);
rev->handler = ngx_http_process_request_line;
ngx_http_process_request_line(rev);
}
最后的 ngx_http_process_request_line(rev) 要在设置给 handler 之后立马调用一次,是因为:
1)改变读事件的 handler,之后再有数据来将会调用 ngx_http_process_request_line
2)epoll 的 ET 触发模式,需要立即处理不然就没有了。
ngx_http_process_request_line()
本函数调用recv读取数据,解析出请求行信息,存在 r->header_in里。使用用无限循环,保证读取完数据,如果返回 again ,说明客户端发送的数据不足,会继续读取。
在某些特殊情况下,比如客户端发来的请求头部特别大,导致之前预分配的 buffer 不够,那么在这个函数中也会增大 buffer。
最后,请求行处理完毕,设置读事件处理函数 ngx_http_process_request_headers()
ngx_http_process_request_headers()
这个函数大体逻辑与前一个 request_line 类似,我在看到这个函数的时候,总是与前面的 ngx_http_process_request_line() 混淆,后来仔细看了下,发现前一个叫 “request line”,这个叫 “request headers”,所以前面一个是值读取 “请求行”,比如 GET/POST 之类的;现在这个函数才是处理其他的头部信息。
在读取了完整的请求头部之后,调用 ngx_http_process_request() 做处理。
ngx_http_process_request()
这个函数主要做了下面几件事。
void ngx_http_process_request(ngx_http_request_t *r) {
c->read->handler = ngx_http_request_handler;
c->write->handler = ngx_http_request_handler;
r->read_event_handler = ngx_http_block_reading;
ngx_http_handler(r);
}
第一行,头部读取完毕之后,把链接的读写事件 handler 都设置为 ngx_http_request_handler,这个函数内实际调用 write_event_handler 或者 read_event_handler。
第三行,把请求的读事件设置为 ngx_http_block_reading(), 注意:第一行是把 “链接” 的 handler,这里是 “请求” 的 handler。
第四行,调用 ngx_http_handler(), 马上开始运行 http 的 11 个 phase
ngx_http_core_run_phases()
ngx_http_handler 中调用这个 run phase 函数,其本身非常简单,就把代码贴在下面,而有关 run 这 11 个 phase,后续的博客再分析。
ngx_http_core_run_phases(ngx_http_request_t *r){
ngx_http_phase_handler_t *ph;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
ph = cmcf->phase_engine.handlers;
while (ph[r->phase_handler].checker) {
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
}
}
以上的函数调用过程,也可以通过 gdb 的 backtrace 得到佐证。
#0 ngx_http_core_run_phases ()
#1 ngx_http_handler ()
#2 ngx_http_process_request ()
#3 ngx_http_process_request_headers ()
#4 ngx_http_process_request_line ()
#5 ngx_http_wait_request_handler ()
#6 ngx_epoll_process_events ()
#7 ngx_process_events_and_timers ()
#8 ngx_single_process_cycle ()
#9 main (argc=3, argv=<optimized out>)