综述
为了节省大家时间和提高效率,这里给出两篇非常不错的文章,出自同一人(应该是雕梁)之手:
1. nginx接受连接,请求处理的前置工作处理
http://www.pagefault.info/?p=205
2. 正式进行request请求处理
http://simohayha.iteye.com/blog/632115
这两篇文章基本上将nginx从连接建立到请求解析过程的绝大部分内容都讲清楚了,这里我想针对里面有些细节,或者是作者没有提到的一些重点地方,跟大家一起分享下。
这里以文章1中的一个流程图开始:
1. ngx_accept
(1) 在ngx_event_process_init,即事件初始化阶段,会设置读事件(这里肯定是针对accpet了)的处理函数为ngx_event_accept,也就是说,当客户端连接到来时,触发的read事件,nginx就可以accpet这个连接了。
(2) 这里我们忽略非epoll的条件判断,我们看到ev->available = ecf->multi_accept;
multi_accept是一个事件模块的一个配置指令,它的作用是控制accept的次数,即在每次新连接到来时,是获取一个连接还是accpet多个(并发连接较多时),考虑到多个worker进程的负载问题,如果multi_accept配置过大,可能使短时间一个进程过于忙绿,所以默认下,是不开启的,即一次只accpet一个连接。
(3) accept4系统调用
在nginx 0.9.0版本里面新增了accpet4的系统调用,这个系统调用比较新,好像是出现在2.6.28内核中,有趣的是正是上述文章的作者提交了这一patch,并最终得到了Igor的认可,呵呵。
这里简单说一下,这个系统调用的作用。一般我们在accept后,会对新的client fd做一些设置,如通过fcnt设置FD_CLOEXEC或者O_NONBLOCK等等,在accept4中的最后一个参数就是用来设置这两个参数:
intaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
flags:SOCK_NONBLOCKor SOCK_CLOEXEC
为什么只有这两个flag吗?不清楚,我个人认为是这两个选项比较常用,这样一来的目的就可以让开发者少敲代码,降低出错的可能吧==!
2. ngx_http_init_connection
ngx_event_accept中通过ls->handler(c),来调用了ngx_http_init_connection。这里的工作中,有个设置write->handler的操作:
// ngx_http_empty_handler基本上是个空操作,除了打印一些信息之外,其他什么也没做。
// 这样设计的意图在于,在处理请求的read事件阶段,没有需要write的动作,
// 如果确实得到了write事件,那么就忽略它了。
c->write->handler= ngx_http_empty_handler;
3. ngx_http_init_request
在这个函数中涉及到一个问题,就是如何根据一个接收到的连接,来确定要使用哪个server conf的配置。这里给出一个对这个问题阐述比较好的文章:
http://bollaxu.iteye.com/blog/859168
在之后设置请求内容的处理函数:
rev->handler= ngx_http_process_request_line;
r->read_event_handler= ngx_http_block_reading;
上面有个read_event_handler的设置,ngx_http_block_reading函数主要是移除read事件。这样做的目的是,是为了在处理当前的request时间内,暂时停止处理新的请求,因为同一连接内会有有多个所谓的pipeline请求。而什么时间来继续处理新请求,由程序来控制,需要的时候再处理(如在keepalive相关处理)。
后面的内容,如request的解析,run_phases 中handler的处理等等,在前面介绍的作者的blog中都有很详细的分析,大家可以参考下:
http://simohayha.iteye.com/category/101180