您的位置:首页 > 运维架构 > Nginx

nginx静态模块分析

2017-01-15 17:12 387 查看
        在上一篇文章中,已经分析了http框架是如何调度各个http模块,共同协作完成一个请求的。现在分析静态模块,看下这个具体的http模块做了些什么。ngx_http_static_module静态模块会从客户端发来的http请求行中获取到文件名(例如: /GET /home/abc.txt  HTTP/1.1),在服务器中查找相应的文件名,并给客户端返回文件内容。在ngx_http_block函数中,将会把需要介入11个http请求阶段的所有http模块回调注册到数组中保存。

//开始解析http块
static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
//http配置解析完成后的后续处理,使得各个http模块可以介入http阶段
for (m = 0; ngx_modules[m]; m++)
{
//只注册http模块
if (ngx_modules[m]->type != NGX_HTTP_MODULE)
{
continue;
}
module = ngx_modules[m]->ctx;
//将模块注册到11个http处理阶段中的某个阶段
if (module->postconfiguration)
{
if (module->postconfiguration(cf) != NGX_OK)
{
return NGX_CONF_ERROR;
}
}
}
}
        静态模块的postconfiguration回调为:ngx_http_static_init,这个函数负责将静态模块的回调注册到数组中保存。

//静态模块将自己注册到11个http请求阶段中的NGX_HTTP_CONTENT_PHASE阶段
static ngx_int_t ngx_http_static_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_CONTENT_PHASE].handlers);
if (h == NULL)
{
return NGX_ERROR;
}
//静态模块在NGX_HTTP_CONTENT_PHASE阶段的处理方法
*h = ngx_http_static_handler;

return NGX_OK;
}
        而NGX_HTTP_CONTENT_PHASE处理阶段的checker回调为:ngx_http_core_content_phase, 这个回调会调用介入这个阶段的所有http模块。
//NGX_HTTP_CONTENT_PHASE阶段的checker方法
ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
//调用模块的处理方法
rc = ph->handler(r);
return NGX_OK;
}
        而这个checker回调又是被ngx_http_core_run_phases调用。 在上一篇文件已经分析过了,这里就不在重复了。       

        现在看下ngx_http_static_handler函数的处理流程。在这个函数里,会根据http请求行中的uri信息(例如:GET /home/abc.txt HTTP/1.1, 则文件扩展名为txt),在服务器中查找相应的文件并打开。然后调用各个过滤器模块,先给客户端发送http响应头部信息,在给客户端发送http响应包体,也就是文件内容。

//函数名:http静态模块处理函数
//功能: 1、根据http请求行中的uri信息,在服务器目录找到对应的文件并打开。
//		2、然后给客户端发送http响应头部
//		3、最后给客户端发送http响应包体,也就是文件内容
static ngx_int_t ngx_http_static_handler(ngx_http_request_t *r)
{
//根据请求的uri获取到完整的绝对路径,保存在path中
last = ngx_http_map_uri_to_path(r, &path, &root, 0);
path.len = last - path.data;

//打开path文件,并获取文件信息保存到of中
ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool

//静态文件,不允许为post方法
if (r->method & NGX_HTTP_POST)
{
return NGX_HTTP_NOT_ALLOWED;
}

//丢弃包体,即便客户端发来了包体内容,这里也不会处理,把它丢弃。为什么呢?因为对于静态资源文件请求,nginx服务器在获取到请求行,请求头部后,就可以调用静态模块查找文件,并给客户端返回文件信息。 不需要接收任何来自客户端的请求包体内容
rc = ngx_http_discard_request_body(r);

//构造http响应头部
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = of.size;
r->headers_out.last_modified_time = of.mtime;

//根据http请求中的uri扩展名参数,从而获取到文件类型
if (ngx_http_set_content_type(r) != NGX_OK)
{
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}

//创建http响应包体发送缓冲区
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
if (b->file == NULL)
{
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
//调度各个过滤器模块互相协作,发送http头部。 过滤器模块将在下一篇文章进行分析
rc = ngx_http_send_header(r);

//header_only为1,表示只有头部信息,不需要发送包体。通常在第一个过滤器中,如果客户端请求的
//文件没有被修改时,则第一个过滤器会设置响应码为304,表示不需要发送包体,在最后一个
//头部顾虑器中检测到状态响应码为304,会将header_only设为1
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
{
return rc;
}
//设置发送缓冲区文件内容
b->file_pos = 0;
b->file_last = of.size;
b->in_file = b->file_last ? 1: 0;
b->last_buf = (r == r->main) ? 1: 0;
b->last_in_chain = 1;
b->file->fd = of.fd;
b->file->name = path;
out.buf = b;
out.next = NULL;
//调度各个过滤器模块互相协作,发送http包体内容。 过滤器模块将在下一篇文章进行分析
return ngx_http_output_filter(r, &out);
}
        ngx_open_cached_file函数涉及到文件缓存,这里暂时抛开文件缓存不管,以后专门有一篇文章来分析缓存管理。这个函数会调用ngx_open_and_stat_file函数,打开文件,并获取文件信息。

//打开文件并获取文件新,保存到of中
static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log)
{
//打开文件
fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, NGX_FILE_OPEN, 0);
//获取文件信息
ngx_fd_info(fd, &fi);
//保存文件信息
of->uniq = ngx_file_uniq(&fi);
of->mtime = ngx_file_mtime(&fi);
of->size = ngx_file_size(&fi);
of->fs_size = ngx_file_fs_size(&fi);
of->is_dir = ngx_is_dir(&fi);
of->is_file = ngx_is_file(&fi);
of->is_link = ngx_is_link(&fi);
of->is_exec = ngx_is_exec(&fi);
}
        其中ngx_http_set_content_type会根据http请求的uri扩展名,查找哈希表从而得到http响应内容的类型。
//根据http请求中的uri扩展名参数,从而获取到文件类型
//例如:GET /home/abc.txt HTTP/1.1,则exten=txt
ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r)
{
//根据uri扩展名进行查找
if (r->exten.len)
{
hash = 0;
for (i = 0; i < r->exten.len; i++)
{
c = r->exten.data[i];
//扩展名只有有一个字符为大小,则转换小写。否则扩展名仍然为小写
if (c >= 'A' && c <= 'Z')
{
exten = ngx_pnalloc(r->pool, r->exten.len);
hash = ngx_hash_strlow(exten, r->exten.data, r->exten.len);
r->exten.data = exten;
break;
}
//计算哈希值
hash = ngx_hash(hash, c);
}
//根据扩展名查找哈希表,进而获取到文件类型
type = ngx_hash_find(&clcf->types_hash, hash,
r->exten.data, r->exten.len);

//设置文件类型
if (type)
{
r->headers_out.content_type_len = type->len;
r->headers_out.content_type = *type;

return NGX_OK;
}
}
//执行到这里,说明没有找到文件类型,则使用默认的文件类型
r->headers_out.content_type_len = clcf->default_type.len;
r->headers_out.content_type = clcf->default_type;

return NGX_OK;
}
        那这个扩展名哈希表什么时候创建的呢? 在解析配置文件后,并将http块与server块的location信息进行合并时,会创建这样的哈希表。ngx_http_core_merge_loc_conf函数负责合并,也是在这个函数里面将nginx服务器默认支持的文件扩展名加入到哈希表中保存。哈希表的处理流程还是挺复杂的,前面花了三篇文章来分析。如果不太清楚哈希表的操作流程,可以参考:nginx普通哈希表nginx通配符哈希表nginx哈希表数组的构造这三篇文章
//location合并
static char * ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
if (conf->types == NULL)
{
//创建哈希数组,存放每一种支持的文件扩展名类型
conf->types = ngx_array_create(cf->pool, 3, sizeof(ngx_hash_key_t));
if (conf->types == NULL)
{
return NGX_CONF_ERROR;
}

//将默认支持的文件扩展名类型插入到哈希表中
for (i = 0; ngx_http_core_default_types[i].key.len; i++)
{
type = ngx_array_push(conf->types);
if (type == NULL)
{
return NGX_CONF_ERROR;
}

type->key = ngx_http_core_default_types[i].key;
type->key_hash =
ngx_hash_key_lc(ngx_http_core_default_types[i].key.data,
ngx_http_core_default_types[i].key.len);
type->value = ngx_http_core_default_types[i].value;
}
}
if (conf->types_hash.buckets == NULL)
{
//用上面的数组初始化哈希表
types_hash.hash = &conf->types_hash;
types_hash.key = ngx_hash_key_lc;
types_hash.max_size = conf->types_hash_max_size;
types_hash.bucket_size = conf->types_hash_bucket_size;
types_hash.name = "types_hash";
types_hash.pool = cf->pool;
types_hash.temp_pool = NULL;
//初始化哈希表
if (ngx_hash_init(&types_hash, conf->types->elts, conf->types->nelts)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
}
        至此,静态模块的处理流程已经分析完成了。在这个模块中,调用了ngx_http_send_header,  ngx_http_output_filter这两个http框架实现的过滤器模块函数,分别用于向客户端发送http响应头部,http响应包体。下一篇文件将详细分析过滤器模块的实现,当然也会分析这两个函数的实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息