异想天开

What's the true meaning of light, Could you tell me why

nginx的http配置文件解析-nginx源码阅读5

日期:2014-06-04 17:31:49
  
最后更新日期:2015-09-27 17:44:08
【技术文章,非码农勿入】
【最后修订时间-2014.07-03】
默认配置文件仅开启http服务,打开默认的/usr/local/nginx/conf/nginx.conf配置文件,大概如下形式:
[code lang="cpp"]
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 80;
location / {
root html;
index index.html
}
}
}
[/code]
一.分层次解析 简化配置文件为如下层次:
[code lang="cpp"]
...
http {
server {
location {
}
}
}
[/code]
http以外的配置级别为main。处于配置文件main级别的典型配置如:
[code lang="cpp"]
worker_processes 1;
events {
worker_connections 1024;
}
[/code]
解析配置文件的典型场景:
ngx_init_cycle函数用main级别(设置type为NGX_MAIN_CONF)解析配置文件/usr/local/nginx/conf/nginx.conf。解析函数为ngx_conf_parse。
1.解析到http字符串后,即处于http main级别,调用回调函数ngx_http_block,备份当前conf,设置type为NGX_HTTP_MAIN_CONF级别,间接递归调用ngx_conf_parse解析http块。
2.解析到server字符串时,即处于http server级别,调用回调函数ngx_http_core_server,备份当前conf,设置type为NGX_HTTP_SRV_CONF级别,间接递归调用ngx_conf_parse解析server块。
3.解析到location字符串时,即处于http location级别,调用回调函数ngx_http_core_location,备份当前conf,设置type为NGX_HTTP_LOC_CONF级别,间接递归调用ngx_conf_parse解析location块。
注:
a.如何找到命令对应的回调函数?
在src/core/ngx_conf_file.c里面ngx_conf_handler函数name后面添加赋值语句,如:
[code lang="cpp"]
if(ngx_strcmp(name->data,"location")==0){
bz_debug = 6 ;
}
[/code]
然后在if后面设条件断点: b xxx if bz_debug==6。需要增加全局变量bz_debug。替换命令名即获得其他回调函数。
b.ngx_command_s命令结构如下
[code lang="cpp"]
struct ngx_command_s {
ngx_str_t name;
ngx_uint_t type;
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_uint_t conf;
ngx_uint_t offset;
void *post;
};
[/code]
命令回调函数的参数conf指针表示什么?
设置为main级别时,则命令回调函数参数conf,即为模块的上下文指针。
若非main级别,则命令回调函数参数conf,即偏移模块的上下文结构ngx_http_conf_ctx_t指针到对应配置项(main_conf,srv_conf,loc_conf)地址处。
c.对于解析不同的级别时,都备份了当前ngx_conf_t的配置。更改ngx_conf_t的ctx的值进入下一级别的配置解析。
main级别解析时,conf(类型为ngx_conf_t)的ctx值为init cycle里面的上下文,即cycle->conf_ctx,为一个指针数组的首地址。
http main级别会构造ngx_http_conf_ctx_t的存储,包含了http main,http server,http location的配置。http main级别的配置包含了一个或多个server的配置信息,如http和https服务器。
http server级别会构造ngx_http_conf_ctx_t中http server,http location的配置。
http location级别会构造ngx_http_conf_ctx_t中的http location配置。

二.nginx服务器怎么监听端口以及其event loop
通常tcp服务器的流程为:create socket->bind host->listen port->event loop,event loop中包含accept connection以及handle session的过程。http server级别下的ngx_conf_parse解析到listen指令时,回调ngx_http_core_listen函数解析server的监听ip和port。因为可能有多个server,多个server的配置可能有冲突,所以回到http main级别(具体流程为ngx_http_block调用ngx_http_optimize_servers)设置ip和port(即初始化ngx_cycle_t的listening数组中元素,设置listen结构的handler为ngx_http_init_connection)。而且知道这点也就理解了为什么某些函数需要带上ngx_http_core_main_conf_t指针作为参数。而ngx_init_cycle中ngx_open_listening_sockets调用操作系统的API接口真正绑定ip和port(可能这里以后代码逻辑若有变化,直接设置断点,然后打印执行路径即可)。

怎么初始化epoll?
这里没有讨论更普遍的怎么初始化网络io事件模型,而是选择前提为在linux平台支持epoll事件模型下怎么初始化epoll讨论,配置文件为文章开头给出的默认配置文件。
events命令对应的ngx_events_block函数。这里会遍历执行所有事件模块的init_conf函数,nginx事件模块的init_conf函数ngx_event_core_init_conf会根据运行环境初始化默认使用网络io事件模型。若没有use指令,则最后使用的事件模型为默认选择的模型。核心初始化函数ngx_init_cycle会触发所有模块的init_module方法。master进程拉起work进程后,会执行每个模块的init_process方法。而事件模块的init_process方法为ngx_event_process_init。该过程包含epoll_create的调用。nginx解析配置文件阶段用一个ngx_listening_t结构表示监听ip:port相关的信息。事件模块初始化(ngx_event_process_init)的时候,ngx_connection_t表示连接相关的信息,对所有用来监听的socket fd关联一个ngx_connection_t结构,初始读事件handler设置为ngx_event_accept。ngx_connection_t结构作为epoll的epoll_event中的附加信息。


怎么accept connection?
一个连接过来,触发ngx_worker_process_cycle大循环中事件模型处理函数ngx_epoll_process_events的epoll_wait返回,简化而言,会执行读事件的handler即ngx_event_accept创建连接。ngx_event_accept添加ngx_connection_t对象到epoll的事件模型(ngx_epoll_add_connection),执行监听结构体的handler函数即ngx_http_init_connection。不过,ngx_http_init_connection有个地方没看懂,添加了一个定时器事件,是超时事件么?下次再看吧。