原创:https://blog.youkuaiyun.com/ndzjx/article/details/89222809
HTTP框架大致由1个核心模块(ngx_http_module)、两个HTTP模块(ngx_http_core_module、ngx_http_upstream_module)组成,它将负责调度其他HTTP模块来一起处理用户请求。首要任务是通过ngx_http_module_t接口中的方法来管理所有HTTP模块的配置项。
Server虚拟主机会以散列表的数据结构组织起来,高效查询。
Location表达式会以一个静态的二叉查找树组织起来。不使用红黑树,是因为location是从nginx.conf中读取到的,是静态不变的,不存在运行过程中添加删除的场景,而且红黑树的查询效率也没有重新构造的静态的完全平衡二叉树高。(ngx_http_init_locations/nginx_http_init_static_location_trees)
typedef struct {
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
void *(*create_main_conf)(ngx_conf_t *cf);
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
void *(*create_srv_conf)(ngx_conf_t *cf);
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
void *(*create_loc_conf)(ngx_conf_t *cf);
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
1:处理http{}块内的main级别配置项时,对每个HTTP模块,都会调用create_main_conf、create_srv_conf、create_loc_conf建立3个结构体,这是为了把同时出现在http{},server{},location{}内的相同配置项进行合并而做的准备。
2:处理server{}内的srv级别配置项时,需要调用每个HTTP模块的create_srv_conf、create_loc_conf
3:处理location{}内的loc级别配置项时,调用HTTP每个模块的create_loc_conf方法建立结构体
解析http{}
ngx_http_core_module模块完成了HTTP框架的大部分功能,而它又是一个HTTP模块,使用到了比较关心的3个结构体(ngx_http_core_main_conf_t、ngx_http_core_srv_conf_t、ngx_http_core_loc_conf_t)
typedef struct {
void **main_conf;
void **srv_conf;
void **loc_conf;
} ngx_http_conf_ctx_t;
通过ngx_cycle_t找到main级别的配置结构体:
#define ngx_http_cycle_get_module_main_conf(cycle, module) \
(cycle->conf_ctx[ngx_http_module.index] ? \
((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index]) \
->main_conf[module.ctx_index]: \
NULL)
解析server块
首先会像解析http块一样,建立属于这个server块的ngx_http_conf_ctx_t结构体。
如果server{}内没有解析到listen配置项,默认监听端口80,当进程没有权限监听1024以下的端口时,则会监听8000端口。
ngx_http_core_module模块的ngx_http_core_main_conf_t结构中有一个servers动态数组。
解析location配置块
仍然会像解析http块一样,先建立ngx_http_conf_ctx_t结构体。
同一个server下的location, ngx_http_core_loc_conf_t结构体构成一个双向链表。(非侵入式链表)
location下还可以嵌套location,同样,会生成链表。
监听端口的管理:
监听端口属于server虚拟主机,由listen配置项决定,与server{}块对应的ngx_http_core_srv_conf_t结构体密切相关。每一个TCP端口,都将使用一个独立的ngx_http_conf_port_t结构体表示。
ngx_http_conf_port_t的addrs动态数组可能不太容易理解。如:对同一个端口8000,我们可以同时监听127.0.0.1:8000、173.39.160.51:8000这两个地址,当一台物理机器具备多个IP地址时这是很有用的
监听端口与server虚拟主机的关系:
HTTP请求的11个阶段:
1)post_read : 接受到完整的http头部后处理的阶段
2)server_rewrite:URI与location匹配前,可以修改URI(重定向)
3)find_config:根据URI寻找匹配的location表达式
4)rewrite:location匹配后可以再修改URI
5)post_rewrite:rewrite修改URI后,防止错误的nginx.conf配置导致死循环,如果一个请求超过10次重定向,就认为i进入了rewrite死循环。
6)pre_access:处理访问权限前,可以介入的阶段
7)access:是否允许请求访问Nginx服务器
8)post_access:如果不允许访问,向用户发送拒绝服务的错误响应。
9)pre_content:包含try_files指令,访问静态文件资源
10)content:处理HTTP请求内容的阶段
11)log:处理完请求后记录日志的阶段
其中:find_config、post_rewrite、post_access、pre_content 4个阶段不允许HTTP模块加入自己的ngx_http_handler_pt方法处理用户请求,它们仅由HTTP框架实现。
struct ngx_http_phase_handler_s {
ngx_http_phase_handler_pt checker; // 由ngx_http_core_module实现
ngx_http_handler_pt handler;
ngx_uint_t next; // 使得处理阶段不必按顺序依次执行
};
ngx_http_phase_engine_t结构体就是所有ngx_http_phase_handler_t组成的数组。此结构体在全局的ngx_http_core_main_conf_t结构体中。ngx_http_core_main_conf_t中还有一个phases数组,保存11个阶段每个阶段要执行的handlers
typedef struct {
ngx_http_phase_handler_t *handlers;
ngx_uint_t server_rewrite_index; // 跳转到server_rewrite阶段处理
ngx_uint_t location_rewrite_index; // 跳转的rewrite
} ngx_http_phase_engine_t;
向指定阶段添加handler的方法大致如下:
在ngx_http_module_t接口的postconfiguration方法中将自定义的方法添加到handler动态数组中。
h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_realip_handler;
其中,content阶段还有一个独有的方法可以添加handler:
把希望处理请求的ngx_http_handler_pt方法设置到location相关的ngx_http_core_loc_conf_t结构体的handler指针中。
好处是:
处理方法不再应用于所有HTTP请求,仅URI匹配时才会被调用。
注意:
这种情况下,每个content阶段只能有一个ngx_http_handler_pt处理方法。这种方式优先于使用postconfiguration设置的方法,postconfiguration设置的方法会失效。
ngx_http_block方法,包括了HTTP框架的完整初始化流程: