Chinaunix首页 | 论坛 | 博客
  • 博客访问: 428501
  • 博文数量: 83
  • 博客积分: 2622
  • 博客等级: 少校
  • 技术积分: 1345
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-17 08:59
个人简介

一直在努力

文章分类

全部博文(83)

文章存档

2014年(3)

2013年(9)

2012年(46)

2010年(25)

分类: 系统运维

2012-05-08 14:07:30

最近需要研究Nginx HTTP请求处理的流程,找到一篇细节文章
Nginx在7层负载交换、反向代理服务领域使用比较广泛。Nginx的结构也比较简单,除了底层几个核心的模块(如ngx_core_module,ngx_event_core_module,ngx_errlog_module等)之外,其它的主要是基于上述核心模块的http和mail的模块组,负责处理相关服务。而这些模块也可以在编译的时候被enable/disable,取决于对实际功能的需求。在这里,我来分析一下Nginx用的最多的功能,即处理http请求的工作流程。

在事件处理的分析中,提到过当有http请求过来时事件的触发和处理过程。我们知道,在一个子进程accept()请求之后,会调用ngx_http_init_connection()函数。这个函数会添加一个读事件,并设置其handler为ngx_http_init_request()。但是,对于http模块的载入以及初始化,却是要从http_block()开始。在父进程(master process)调用ngx_init_cycle()的时候,会调用一次ngx_conf_parse()函数(先不在这里分析),这个时候(解析到了"http {...}" block),ngx_http_module模块的set()函数即ngx_http_block()就被调用。

C代码  收藏代码
  1. static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)  
  2. {  
  3.     /* the main http context */  
  4.     //它里面只有三个成员:(void**)main_conf,(void**)srv_conf和(void**)loc_conf,注意它们是双层指针  
  5.     //每个NGX_HTTP_MODULE模块都有一个main_conf[i],srv_conf[i]和loc_conf[i]指向相应的上下文,但不是每个都会用到。  
  6.     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));  
  7.     *(ngx_http_conf_ctx_t **) conf = ctx;  
  8.     /* 清点NGX_HTTP_MODULE类型模块个数并给每个模块设定索引(index) */  
  9.     ngx_http_max_module = 0;  
  10.     for (m = 0; ngx_modules[m]; m++) {  
  11.         if (ngx_modules[m]->type != NGX_HTTP_MODULE) {  
  12.             continue;  
  13.         }  
  14.         ngx_modules[m]->ctx_index = ngx_http_max_module++;  
  15.     }  
  16.     //main_conf在http{...}里面的所有上下文中都是一致的  
  17.     //分配内存,个数为http模块的个数  
  18.     ctx->main_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);  
  19.   
  20.     //用来合并server{...}里面的srv_conf  
  21.     //分配内存,个数为http模块的个数  
  22.     ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);  
  23.       
  24.     //用来合并 /loc {...} 里面的loc_conf  
  25.     //分配内存,个数为http模块的个数  
  26.     ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);  
  27.   
  28.     //如有定义的话,调用每个http模块里面的create_main_conf(),create_srv_conf()和create_loc_conf()  
  29.     for (;;) {  
  30.         //如果是NGX_HTTP_MODULE模块  
  31.         module = ngx_modules[m]->ctx;//module的上下文,为ngx_http_module_t类型  
  32.         mi = ngx_modules[m]->ctx_index;//在http模块组里的索引  
  33.         //如有的话,就调用每个模块的下列函数,并存入ctx相应的索引位  
  34.         //一般在 http {...}里面而不在 server {...}里面有命令的模块有create_main_conf()  
  35.         //一般在 server {...}里面而不在 /loc {...}里面有命令的模块有create_srv_conf()  
  36.         //一般在 /loc {...}里面有命令的模块有create_loc_conf()  
  37.         ctx->main_conf[mi] = module->create_main_conf(cf);//创建模块自定的上下文,存入相应位置  
  38.         ctx->srv_conf[mi] = module->create_srv_conf(cf);//创建模块自定的srv上下文,存入相应位置  
  39.         ctx->loc_conf[mi] = module->create_loc_conf(cf);//创建模块自定的loc上下文,存入相应位置  
  40.     }  
  41.     pcf = *cf;  
  42.     cf->ctx = ctx;//把ctx放入cf  
  43.     for (;;) {  
  44.         //调用每个NGX_HTTP_MODULE模块的preconfiguration()函数  
  45.         //一般模块的preconfiguration()作用是添加一些模块要用的变量到ngx_http_core_main_conf_t的hash表variables_keys  
  46.         module->preconfiguration(cf);  
  47.     }  
  48.     /* parse inside the http{} block */  
  49.     cf->module_type = NGX_HTTP_MODULE;  
  50.     cf->cmd_type = NGX_HTTP_MAIN_CONF;  
  51.     rv = ngx_conf_parse(cf, NULL);//会调用指令的set()函数,如果有嵌套的block会继续调用ngx_conf_parse()。大部分的http{}(但在server{}外)的指令都是给ngx_http_core_loc_conf_t的成员赋值。  
  52.     //获取ngx_http_core_module(定义一些http公用的命令和变量)的main_conf  
  53.     cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];  
  54.     cscfp = cmcf->servers.elts;//cmcf->servers在ngx_http_core_create_main_conf里面初始化并分配内存,并在server{} block里面读取配置并设置相关变量  
  55.     for (;;) {  
  56.         //调用每个NGX_HTTP_MODULE模块的init_main_conf(),调用ngx_http_merge_servers()  
  57.         rv = module->init_main_conf(cf, ctx->main_conf[mi]);//对main_conf上下文的一些成员变量做初始化  
  58.         rv = ngx_http_merge_servers(cf, cmcf, module, mi);//每个server{} block都有一个srv_conf(继承ngx_http_core_module的srv_conf),且其ctx变量指向一组main_conf[],srv_conf[]和loc_conf[],储存了各个模块的main_conf,srv_conf,loc_conf(但是只有在server{}中调用模块的指令,才会用到这些xxx_conf)  
  59.     }  
  60.     /* create location trees */  
  61.     //给每个server创建location tree  
  62.     for (s = 0; s < cmcf->servers.nelts; s++) {  
  63.         clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];//每个srv(虚拟主机)的ngx_http_core_loc_conf_t  
  64.         //location根据字母顺序排序  
  65.         ngx_http_init_locations(cf, cscfp[s], clcf);  
  66.         //建立静态location树(三叉树)  
  67.         //在http://blog.csdn.net/benbendy1984/archive/2010/11/18/6019336.aspx有简单介绍  
  68.         ngx_http_init_static_location_trees(cf, clcf);  
  69.     }  
  70.     //初始化几个phase的handler(分配内存)  
  71.     if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {  
  72.         return NGX_CONF_ERROR;  
  73.     }  
  74.     //初始化header的hash table  
  75.     if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {  
  76.         return NGX_CONF_ERROR;  
  77.     }  
  78.     for (;;) {  
  79.         //注册每个模块对应phase的处理函数  
  80.         //这儿的处理函数是通用的(如http模块的所有location)  
  81.         //特定location的处理函数在指令(directive)的set()里面设置  
  82.         module->postconfiguration(cf);  
  83.     }  
  84.     //设置pre-defined variables的get_handler等,并放入hash table里  
  85.     if (ngx_http_variables_init_vars(cf) != NGX_OK) {  
  86.         return NGX_CONF_ERROR;  
  87.     }  
  88.     //注册各http模块phase的checker,并把postconfiguration注册的handler都放到cmcf的phase_engine,phase_engine把cheker和对应的handler一同放入数组,并通过next变量链接,供之后处理请求的时候按phase顺序调用。  
  89.     //在http://simohayha.iteye.com/blog/670326有比较详细的介绍  
  90.     if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {  
  91.         return NGX_CONF_ERROR;  
  92.     }  
  93.     //把server_name储存在hash table里,最后给每个listening socket注册ls->handler=ngx_http_init_connection;  
  94.     //详见http://blog.csdn.net/ccdd14/archive/2010/09/12/5878459.aspx  
  95.     if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {  
  96.         return NGX_CONF_ERROR;  
  97.     }  
  98.     return NGX_CONF_OK;  
  99. }  
 


当nginx收到请求的时候,就会调用(1)ngx_http_init_request,开始了对请求的处理过程。

C代码  收藏代码
  1. static void ngx_http_init_request(ngx_event_t *rev)  
  2. {  
  3.     //获取event的connection  
  4.     c = rev->data;  
  5.     //如果事件超时  
  6.     //...  
  7.   
  8.     hc = c->data;  
  9.     //给这个ngx_http_connection_t类型的指针分配内存  
  10.     //...  
  11.   
  12.     r = hc->request;  
  13.     //创建(re-init)一个新的request  
  14.     //...  
  15.   
  16.     c->data = r;  
  17.     r->http_connection = hc;  
  18.     c->sent = 0;  
  19.     r->signature = NGX_HTTP_MODULE;  
  20.   
  21.     /* find the server configuration for the address:port */  
  22.     port = c->listening->servers;//在http_block()的时候就已经设置完成(在"listen"指令和ngx_http_optimize_servers函数里)  
  23.     r->connection = c;  
  24.   
  25.     //如果有多个监听的addr  
  26.     if (port->naddrs > 1) {  
  27.         /* 
  28.         * there are several addresses on this port and one of them 
  29.         * is an "*:port" wildcard so getsockname() in ngx_http_server_addr() 
  30.         * is required to determine a server address 
  31.         */  
  32.         if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {  
  33.             ngx_http_close_connection(c);  
  34.             return;  
  35.         }  
  36.         switch (c->local_sockaddr->sa_family) {  
  37.             default/* AF_INET */  
  38.                 sin = (struct sockaddr_in *) c->local_sockaddr;  
  39.                 addr = port->addrs;  
  40.                 /* the last address is "*" */  
  41.                 for (i = 0; i < port->naddrs - 1; i++) {  
  42.                     if (addr[i].addr == sin->sin_addr.s_addr) {  
  43.                         break;  
  44.                     }  
  45.                 }  
  46.                 addr_conf = &addr[i].conf;  
  47.                 break;  
  48.         }  
  49.     }  
  50.     else {  
  51.         switch (c->local_sockaddr->sa_family) {  
  52.             default/* AF_INET */  
  53.                 addr = port->addrs;  
  54.                 addr_conf = &addr[0].conf;  
  55.                 break;  
  56.         }  
  57.     }  
  58.     r->virtual_names = addr_conf->virtual_names;  
  59.     /* the default server configuration for the address:port */  
  60.     cscf = addr_conf->core_srv_conf;  
  61.     r->main_conf = cscf->ctx->main_conf;  
  62.     r->srv_conf = cscf->ctx->srv_conf;  
  63.     r->loc_conf = cscf->ctx->loc_conf;  
  64.     //event的handler设为ngx_http_process_request_line  
  65.     rev->handler = ngx_http_process_request_line;  
  66.     //设置request的读事件  
  67.     r->read_event_handler = ngx_http_block_reading;  
  68.     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);  
  69.     c->log->file = clcf->error_log->file;  
  70.   
  71.     r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);  
  72.     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);  
  73.     //分配内存给variables  
  74.     r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts *   
  75.                     sizeof(ngx_http_variable_value_t));  
  76.     c->single_connection = 1;  
  77.     c->destroyed = 0;  
  78.   
  79.     //对r的其它的初始化  
  80.     r->main = r;//主请求  
  81.     r->method = NGX_HTTP_UNKNOWN;          
  82.     r->headers_in.content_length_n = -1;   
  83.     r->headers_in.keep_alive_n = -1;  
  84.     r->headers_out.content_length_n = -1;  
  85.     r->headers_out.last_modified_time = -1;  
  86.     r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;  
  87.     r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1;  
  88.     r->http_state = NGX_HTTP_READING_REQUEST_STATE;  
  89.     ctx = c->log->data;  
  90.     ctx->request = r;  
  91.     ctx->current_request = r;  
  92.     r->log_handler = ngx_http_log_error_handler;  
  93.     rev->handler(rev); //ngx_http_process_request_line  
  94. }  
 

下面,分别调用了下列函数,在这里不一一详细分析:
-->(2)ngx_http_process_request_line()//读取、解析请求行,并存入r的相应成员(如uri,args相关的重要指针)。
-->(3)ngx_http_process_request_headers()//把header line逐行解析并以key-value形式存入r。
-->(4)ngx_http_process_request()
-->(5)ngx_http_handler()
-->(6)ngx_http_core_run_phases()

C代码  收藏代码
  1. void ngx_http_core_run_phases(ngx_http_request_t *r)  
  2. {  
  3.     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);  
  4.     ph = cmcf->phase_engine.handlers;//拿到phase_engine里存放所有handler的数组  
  5.   
  6.     //如果不是内部定向(rewrite)的话,r->phase_handler = 0;  
  7.     //每个phase的checker在ngx_http_init_phase_handlers设置  
  8.     //每个phase的handler在ngx_http_init_phase_handlers设置  
  9.     //ph(ngx_http_phase_handler_s类型)的顺序:越早在postconfiguration()时注册handler的模块,它的handler在ph里面就越靠后被调用。  
  10.     while (ph[r->phase_handler].checker) {  
  11.         //ngx_http_init_phase_handlers里面注册每个phase的checker和handler  
  12.         rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);  
  13.         if (rc == NGX_OK) {  
  14.             return;  
  15.         }  
  16.     }  
  17. }  
 

关于ngx_http_core_main_conf_t,它里面有两个成员,phase_engine和phases。其中phases是一个存放所有模块的在postconfiguration()函数里面注册的相关phase的handler。比如,ngx_http_index_module里面,就push了一个NGX_HTTP_CONTENT_PHASE的handler:ngx_http_index_handler。这个handler就被存放在ngx_http_core_main_conf_t的phases[NGX_HTTP_CONTENT_PHASE]这个数组里面。而phase_engine里面的handlers(ngx_http_phase_handler_s类型)则是一个checker和handler的函数对,next指向下一个phase在phase_engine里面的index。

比如说,最基本的一个处理过程:NGX_HTTP_FIND_CONFIG_PHASE --> NGX_HTTP_PREACCESS_PHASE --> NGX_HTTP_ACCESS_PHASE --> NGX_HTTP_CONTENT_PHASE。被调用的函数是:

C代码  收藏代码
  1. //NGX_HTTP_FIND_CONFIG_PHASE的checker  
  2. ngx_http_core_find_config_phase()  
  3. {  
  4.     //调用ngx_http_core_find_static_location(),比较r的uri和config tree的名字,找到合适的/location,并更新r->loc_conf  
  5.     ngx_http_core_find_location();  
  6.     //基于r->loc_conf,更新r的相应成员变量  
  7.     //其中如果有一些location调用了某些模块的命令设置了特定的clcf->handler,r->content_handler = clcf->handler。在ngx_http_core_content_phase()里面就会被调用。  
  8.     ngx_http_update_location_config();  
  9.     //goto next handler即next phase  
  10.     r->phase_handler++;  
  11. }  
 

NGX_HTTP_PREACCESS_PHASE的checker是ngx_http_core_generic_phase(),即调用ph->handler(r)。在默认的模块配置里,NGX_HTTP_PREACCESS_PHASE类型的有ngx_http_limit_req_module和ngx_http_limit_zone_module两个模块,那么它们的ngx_http_limit_req_handler()和ngx_http_limit_zone_handler()就会被调用。在ngx_http_core_generic_phase()里面调用ph->handler(r)之后即控制下一个被调用的函数(是到下一个phase或者是本个phase的下一个handler)。

C代码  收藏代码
  1. //NGX_HTTP_ACCESS_PHASE的checker  
  2. ngx_http_core_access_phase()  
  3. {  
  4.     rc = ph->handler(r);  
  5.     //根据rc来判断选择是下一个phase或者是本个phase的下一个handler  
  6.     //...  
  7. }  
 

在模块的默认配置里,NGX_HTTP_ACCESS_PHASE类型的是ngx_http_access_module和ngx_http_auth_basic_module,即ph->handler(r)调用了ngx_http_access_handler()和ngx_http_auth_basic_handler()

C代码  收藏代码
  1. //NGX_HTTP_CONTENT_PHASE的checker  
  2. ngx_http_core_content_phase()  
  3. {  
  4.     //如果location有特定的handler  
  5.     //e.g. ngx_http_proxy_module的"proxy_pass"命令就会设置location的handler "ngx_http_proxy_handler"  
  6.     if (r->content_handler) {  
  7.         r->write_event_handler = ngx_http_request_empty_handler;  
  8.         ngx_http_finalize_request(r, r->content_handler(r));  
  9.         return NGX_OK;  
  10.     }  
  11.     //没有location没有特定的handler  
  12.     rc = ph->handler(r);  
  13.     ph++;  
  14.     //跳到下一个NGX_HTTP_CONTENT_PHASE的handler或者下一个phase的checker  
  15.     if (ph->checker) {  
  16.         r->phase_handler++;  
  17.         return NGX_AGAIN;  
  18.     }  
  19.     //...  
  20. }  
 

默认的配置里有3个NGX_HTTP_CONTENT_PHASE类型的模块有ngx_http_static_module,ngx_http_index_module和ngx_http_autoindex_module,调用它们的ngx_http_static_handler(),ngx_http_index_handler()和ngx_http_autoindex_handler()。


通过实际运行的的debug信息,可以看到每个phase的checker被调用时候的handler的r->phase_handler(即index)。根据实际的情况(如根据不同location配置),不一定每个handler都会被调用(通过设置r->phase_handler = ph->next),甚至有时候会直接跳到另外一个phase(比如在ngx_http_index_handler()里面调用ngx_http_internal_redirect(),重新进入ngx_http_handler(),即重新把phase走一遍)。这些都是根据每个phase的handler的实现以及实际的请求来决定的。

关于每个phase的具体功能:
C代码  收藏代码
  1. typedef enum {  
  2.     //0读取请求phase  
  3.     NGX_HTTP_POST_READ_PHASE = 0,  
  4.     //1这个阶段主要是处理全局的(server block)的rewrite。  
  5.     NGX_HTTP_SERVER_REWRITE_PHASE,  
  6.     //2这个阶段主要是通过uri来查找对应的location,然后根据loc_conf设置r的相应变量  
  7.     //e.g. 根据location内配置的具体命令设置r->content_handler,到NGX_HTTP_CONTENT_PHASE调用  
  8.     NGX_HTTP_FIND_CONFIG_PHASE,  
  9.     //3这个主要处理location的rewrite  
  10.     NGX_HTTP_REWRITE_PHASE,  
  11.     //4post rewrite,这个主要是进行一些校验以及收尾工作,以便于交给后面的模块。  
  12.     NGX_HTTP_POST_REWRITE_PHASE,  
  13.     //5比如流控这种类型的access就放在这个phase,也就是说它主要是进行一些比较粗粒度的access。  
  14.     NGX_HTTP_PREACCESS_PHASE,  
  15.     //6这个比如存取控制,权限验证就放在这个phase,一般来说处理动作是交给下面的模块做的.这个主要是做一些细粒度的access  
  16.     NGX_HTTP_ACCESS_PHASE,  
  17.     //7一般来说当上面的access模块得到access_code之后就会由这个模块根据access_code来进行操作  
  18.     NGX_HTTP_POST_ACCESS_PHASE,  
  19.     //8try_file模块,也就是对应配置文件中的try_files指令,可接收多个路径作为参数,当前一个路径的资源无法找到,则自动查找下一个路径  
  20.     NGX_HTTP_TRY_FILES_PHASE,  
  21.     //9内容处理模块  
  22.     NGX_HTTP_CONTENT_PHASE,    
  23.     //10log模块  
  24.     NGX_HTTP_LOG_PHASE  
  25. } ngx_http_phases;  
 

Nginx的http上下文(在读取配置的时候设置):
阅读(1956) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~