一节事件模块好像并没有搞的太懂,由于时间的关系,还是继续到nginx中一个重要的知识点http模块,作为web服务器这个是nginx中最常用的模块了,遇到啥不懂的,再回去看看事件模块了。
10.1 HTTP框架概述这个老生长谈了,不说那么多了,贴代码
static ngx_command_t ngx_http_commands[] = {
{ ngx_string("http"),
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_http_block,
0,
0,
NULL },
ngx_null_command
};
static ngx_core_module_t ngx_http_module_ctx = {
ngx_string("http"),
NULL,
NULL
};
ngx_module_t ngx_http_module = {
NGX_MODULE_V1,
&ngx_http_module_ctx, /* module context */
ngx_http_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
这就是http的核心模块,接下来我们在看看http模块独立的结构体
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;
就是http模块单独的结构体,专门处理配置文件的
接下 在看看配置文件的结构体
typedef struct {
void **main_conf;
void **srv_conf;
void **loc_conf;
} ngx_http_conf_ctx_t;
之前开发过http模块的都知道配置文件有main.srv.loc,这里正好对应这三个结构,接下来就好好分析一下http模块。
10.2 管理HTTP模块的配置项http的配置项确实很多,接下来看看nginx是怎么管理的
10.2.1 管理main级别下的配置项
话不多说上图
10.2.2 管理server级别下的配置项
有点乱,nginx这个配置文件设置的是递归的方式,确实有点难受,不过对着源码看的话,应该还能看的懂。
10.2.3 管理loc级别下的配置项
sever下的loc的配置项,是使用了双向链表来连接起来的
10.2.4 不同级别配置项的合并
先看看调用的时候“
for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = cf->cycle->modules[m]->ctx;
//ctx_index是这个HTTP模块在所有HTTTP模块中的序号
mi = cf->cycle->modules[m]->ctx_index;
//合并这个http块下的第几个配置
rv = ngx_http_merge_servers(cf, cmcf, module, mi);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
看看真正合并的函数
static char *
ngx_http_merge_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
ngx_http_module_t *module, ngx_uint_t ctx_index)
{
char *rv;
ngx_uint_t s;
ngx_http_conf_ctx_t *ctx, saved;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t **cscfp;
cscfp = cmcf->servers.elts;
ctx = (ngx_http_conf_ctx_t *) cf->ctx;
saved = *ctx;
rv = NGX_CONF_OK;
//遍历所有server下对应的ngx_http_core_srv_conf_t结构体
for (s = 0; s < cmcf->servers.nelts; s++) {
/* merge the server{}s' srv_conf's */
ctx->srv_conf = cscfp[s]->ctx->srv_conf;
//进行main和srv合并
if (module->merge_srv_conf) {
rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index],
cscfp[s]->ctx->srv_conf[ctx_index]);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
if (module->merge_loc_conf) {
/* merge the server{}'s loc_conf */
ctx->loc_conf = cscfp[s]->ctx->loc_conf;
//首先是main和loc合并
rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index],
cscfp[s]->ctx->loc_conf[ctx_index]);
if (rv != NGX_CONF_OK) {
goto failed;
}
/* merge the locations{}' loc_conf's */
clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
//server块下的loc与loc合并
rv = ngx_http_merge_locations(cf, clcf->locations,
cscfp[s]->ctx->loc_conf,
module, ctx_index);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
}
failed:
*ctx = saved;
return rv;
}
10.3 监听端口的管理
每监听一个TCP端口,都将使用一个独立ngx_htttp_conf_port_t结构表示,
typedef struct {
ngx_int_t family;
in_port_t port;
ngx_array_t addrs; /* array of ngx_http_conf_addr_t */
} ngx_http_conf_port_t;
这个结构会由全局的ngx_http_core_main_conf_t结构体保存
typedef struct {
ngx_array_t servers; /* ngx_http_core_srv_conf_t */
ngx_array_t *ports;
} ngx_http_core_main_conf_t;
ngx_http_conf_port_t结构中的addrs是一个动态数组,里面存储了监听的多个server地址,我们看看数组成员:
typedef struct {
ngx_http_listen_opt_t opt;
ngx_hash_t hash;
ngx_hash_wildcard_t *wc_head;
ngx_hash_wildcard_t *wc_tail;
#if (NGX_PCRE)
ngx_uint_t nregex;
ngx_http_server_name_t *regex;
#endif
/* the default server configuration for this address:port */
//该监听端口下对应的默认server虚拟主机
ngx_http_core_srv_conf_t *default_server;
//servers动态数组成员指向ngx_http_srv_conf_结构体
ngx_array_t servers; /* array of ngx_http_core_srv_conf_t */
} ngx_http_conf_addr_t;
我们要快速检索sever,是可以通过hash,wc_head。wc_tail成员直接获取的,在前面见过怎么初始化这些哈希表。
loc的快速检索,是在初始化的时候,有动态生成了一个完全平衡的二叉树,因为这是是不需要改变的,所以不需要用红黑树,直接自动生成即可。
10.4 HTTP请求的11个处理阶段HTP框架依据常见的处理流程将处理阶段划分为11个阶段,其中每个处理阶段都可以由任意多个HTP模块流水式的处理请求。
typedef enum {
//在接收到完整的HTTP头部后处理的HTTP阶段
NGX_HTTP_POST_READ_PHASE = 0,
//在将请求的URI与location表达式匹配前,修改请求的URI是一个独立的HTTP阶段
NGX_HTTP_SERVER_REWRITE_PHASE,
//根据请求的URI寻找匹配的loc表达式,这个阶段只能有ngx_http_core_module模块实现
NGX_HTTP_FIND_CONFIG_PHASE,
//在NGX_HTTP_FIND_CONFIG_PHASE阶段寻找到匹配的loc之后再修改请求的URI
NGX_HTTP_REWRITE_PHASE,
//这一阶段是用于在rewrite重写urL后,防止错误的nginx.conf配置导致死循环
NGX_HTTP_POST_REWRITE_PHASE,
//表示在处理ngx_HTTP_ACCESS_PHASE阶段决定请求的访问权限前,HTTP模块可以介入的处理阶段
NGX_HTTP_PREACCESS_PHASE,
//这个阶段用于让HTTP模块判断是否允许这个请求访问nginx服务器
NGX_HTTP_ACCESS_PHASE,
//这个阶段是给NGX_HTTP_ACCESS_PHASE阶段收尾
NGX_HTTP_POST_ACCESS_PHASE,
//
NGX_HTTP_PRECONTENT_PHASE,
//用于处理HTTP请求内容的阶段,这是大部分HTTP模块最愿意介入的阶段
NGX_HTTP_CONTENT_PHASE,
//处理完请求后记录日志阶段
NGX_HTTP_LOG_PHASE
} ngx_http_phases;
我们在回到http初始化过程,
static ngx_int_t
ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,
cf->pool, 2, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_PRECONTENT_PHASE].handlers,
cf->pool, 2, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,
cf->pool, 4, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,
cf->pool, 1, sizeof(ngx_http_handler_pt))
!= NGX_OK)
{
return NGX_ERROR;
}
return NGX_OK;
}
我们来看看phase的意义:
typedef struct {
ngx_array_t servers; /* ngx_http_core_srv_conf_t */
//由下面各阶段处理方法构成的phases数组构建的阶段引擎才是流水式处理HTTP请求的实际数据结构
ngx_http_phase_engine_t phase_engine;
...
ngx_array_t *ports;
//用于在HTTP框架初始化时帮组各个HTTP模块在任意阶段中添加HTTP处理方法
ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];
} ngx_http_core_main_conf_t;
用在HTTP的11个阶段的过程中,插入的回调函数的方法。
在哪里插入到这个结构中呢?
for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = cf->cycle->modules[m]->ctx;
if (module->postconfiguration) {
if (module->postconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
http框架代码会调用postconfiguration这个函数,这时候,需要介入处理的话,就需要实现这个函数,并添加到phase中,我们看看官方有一个实现ngx_http_realip_init:
static ngx_int_t
ngx_http_realip_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_realip_handler;
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_realip_handler;
return NGX_OK;
}
看到的结果就是往这个数组中填充回调函数
我们跟着框架代码继续走,还有这个函数ngx_http_init_phase_handlers处理phase有关的:
static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
ngx_int_t j;
ngx_uint_t i, n;
ngx_uint_t find_config_index, use_rewrite, use_access;
ngx_http_handler_pt *h;
ngx_http_phase_handler_t *ph;
ngx_http_phase_handler_pt checker;
cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
find_config_index = 0;
use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;
n = 1 /* find config phase */
+ use_rewrite /* post rewrite phase */
+ use_access; /* post access phase */
for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
n += cmcf->phases[i].handlers.nelts;
}
ph = ngx_pcalloc(cf->pool,
n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
if (ph == NULL) {
return NGX_ERROR;
}
cmcf->phase_engine.handlers = ph;
n = 0;
for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
h = cmcf->phases[i].handlers.elts;
switch (i) {
case NGX_HTTP_SERVER_REWRITE_PHASE:
if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
cmcf->phase_engine.server_rewrite_index = n;
}
checker = ngx_http_core_rewrite_phase;
break;
case NGX_HTTP_FIND_CONFIG_PHASE:
find_config_index = n;
ph->checker = ngx_http_core_find_config_phase;
n++;
ph++;
continue;
case NGX_HTTP_REWRITE_PHASE:
if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {
cmcf->phase_engine.location_rewrite_index = n;
}
checker = ngx_http_core_rewrite_phase;
break;
case NGX_HTTP_POST_REWRITE_PHASE:
if (use_rewrite) {
ph->checker = ngx_http_core_post_rewrite_phase;
ph->next = find_config_index;
n++;
ph++;
}
continue;
case NGX_HTTP_ACCESS_PHASE:
checker = ngx_http_core_access_phase;
n++;
break;
case NGX_HTTP_POST_ACCESS_PHASE:
if (use_access) {
ph->checker = ngx_http_core_post_access_phase;
ph->next = n;
ph++;
}
continue;
case NGX_HTTP_CONTENT_PHASE:
checker = ngx_http_core_content_phase;
break;
default:
checker = ngx_http_core_generic_phase;
}
n += cmcf->phases[i].handlers.nelts;
for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) {
ph->checker = checker;
ph->handler = h[j];
ph->next = n;
ph++;
}
}
return NGX_OK;
}
我们先看phase_engine这个成员:
typedef struct {
//handler是由ngx_http_phase_handler_t构成的数组首地址,它表示一个请求可能经历的所有ngx_http_handler_pt处理方法
ngx_http_phase_handler_t *handlers;
//表示NGX_HTTP_SERVER_REWRITE_PHASE阶段第1个ngx_http_phase_handler_t处理方法在handlers数组的序号,用在执行请求快速跳转到NGX_HTTP_SERVER_REWRITE_PHASE这个阶段
ngx_uint_t server_rewrite_index;
//表示NGX_HTTP_REWRITE_PHASE阶段第1个
ngx_uint_t location_rewrite_index;
} ngx_http_phase_engine_t;
struct ngx_http_phase_handler_s {
//在处理某一个HTTP阶段时,HTTP框架将会在checker方法已实现的前提下首先调用checker方法来处理请求。而不是直接调用任何阶段中的handler方法,只会在checker方法中才会去调用handler方法
ngx_http_phase_handler_pt checker;
//只能通过定义handler方法才能介入某一个HTTP处理阶段以处理请求
ngx_http_handler_pt handler;
//将要执行下一个HTTP处理阶段的序号
ngx_uint_t next;
};
看到这里才明白,phase_engine控制运行过程中一个HTTP请求所要经过的HTTP处理阶段,而ohase数组更像是一个临时变量,实际上也仅在nginx启动过程中用到。
但是nginx框架并没有直接调用handler,而是调用checker,我们看看一个例子:
ngx_int_t
ngx_http_core_content_phase(ngx_http_request_t *r,
ngx_http_phase_handler_t *ph)
{
size_t root;
ngx_int_t rc;
ngx_str_t path;
if (r->content_handler) {
r->write_event_handler = ngx_http_request_empty_handler;
ngx_http_finalize_request(r, r->content_handler(r));
return NGX_OK;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"content phase: %ui", r->phase_handler);
rc = ph->handler(r); //这个就是绑定的回调函数
if (rc != NGX_DECLINED) {
ngx_http_finalize_request(r, rc);
return NGX_OK;
}
/* rc == NGX_DECLINED */
ph++;
if (ph->checker) {
r->phase_handler++;
return NGX_AGAIN;
}
/* no content handler was found */
if (r->uri.data[r->uri.len - 1] == '/') {
if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"directory index of \"%s\" is forbidden", path.data);
}
ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
return NGX_OK;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");
ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
return NGX_OK;
}
这里是不是奇怪,因为会先判断r->content_handler这个函数,然后再调用我们绑定的handler,这是因为NGX_HTTP_CONTENT_PHASE阶段可以用另种方式介入到这个阶段的处理中,第一种就是我们上面说的,第二种就是我们之前实现的HTTP模块的时候,绑定到ngx_http_core_loc_conf_t结构体的handler指针中,nginx框架会把这个handler绑定到ngx_http_request_t的content_handler,这样就可以做到介入了。
如果希望这个ngx_http_handler_pt方法应用于所有的用户请求,则应该放到phases[NGX_HTTP_CONTENT_PHASE]胴体数组中;反之,如果希望这个方式仅应用于URI匹配了某些location的用户请求,则应该在一个location写的回到方法中。
10.5 HTTP框架的初始化流程懒的写了,就这样了。