上一篇nginx的文章中,我们理解了整个http正向代理的运行流程原理,主要就是事件机制接入,header解析,body解析,然后遍历各种checker,直到处理成功为止。
我们以访问一个普通文件为例,nginx到底是如何找到这个文件并返回信息的呢?它会不会有些什么限制呢?
按我们自己的理解,它应该是uri栏里截取出相应的路径,然后对应到root下,查找到相应文件,返回即可。那么,它又是如何处理html和其他的各种不同格式的文件呢?
就让我们一起来探秘nginx文件的查找实现吧!
0. nginx 静态文件配置
要配置静态文件处理,只需在http server中配置root路径即可。(当然了,你可以根据前缀配置许多不同的root)
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 8085;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location /hello {
root /www/hello;
index index.html index.htm;
}
location / {
root html;
index index.html index.htm;
}
}
# 后续可以添加无数个server 扩展
}
配置简单吧,实际核心就两三行代码搞定:监听端口号 listen、访问域名 server_name、服务器根路径 root。明显这是nginx成功的原因之一。
本文要讨论的场景是,如果我访问 http://localhost:8085/1.txt?d=xxx, nginx将如何干成这件事?
1. checker的遍历回顾
我们先来回顾下,nginx是如何遍历各个checker的吧!
// http/ngx_http_core_module.c
// 响应客户端操作, 多阶段式操作
void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_phase_handler_t *ph;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
ph = cmcf->phase_engine.handlers;
// 依次调用各 checker, 直到有响应 OK 的checker为止
while (ph[r->phase_handler].checker) {
// 每次调用 checker 之后, 内部都会将 r->phase_handler++, 即迭代下一个
// 此处的 checker 非常之多, 是在各模块启动时, 自动向 ngx_http_core_module.main_conf 中进行注册的
/**
* 定义如下:
typedef enum {
NGX_HTTP_POST_READ_PHASE = 0,
NGX_HTTP_SERVER_REWRITE_PHASE,
NGX_HTTP_FIND_CONFIG_PHASE,
NGX_HTTP_REWRITE_PHASE,
NGX_HTTP_POST_REWRITE_PHASE,
NGX_HTTP_PREACCESS_PHASE,
NGX_HTTP_ACCESS_PHASE,
NGX_HTTP_POST_ACCESS_PHASE,
NGX_HTTP_PRECONTENT_PHASE,
NGX_HTTP_CONTENT_PHASE,
NGX_HTTP_LOG_PHASE
} ngx_http_phases;
// 注册方式
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
*h = ngx_http_access_handler;
*/
// 将请求信息和 handler 本身传入调用(不是面向, 只能这么做了)
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
// 只要有一个处理成功, 则后续不再调用
if (rc == NGX_OK) {
return;
}
}
}
可以说,它的各checker是非常复杂的,各模块都可以向其中注册处理器。这也是nginx灵活性的体现。不过我们不想关注太多。
文件的查找是在 static_module 中完成的,我们只需关注这个即可。
2. 静态文件模块的注册
静态文件模块主要就是负责静态文件的查找处理。几乎所有的http处理模块,都是先进行注册,然后再被调用的过程。static_module 自然不例外。
// http/modules/ngx_http_static_handler.c
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);
// 获取http_core_module的配置
// 将ngx_http_static_handler 添加到 NGX_HTTP_CONTENT_PHASE 的handlers中
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_static_handler;
return NGX_OK;
}
// 下面是一些nginx的模块暴露规范,只有这样,这个模块才会被接入到整个系统中
static ngx_http_module_t ngx_http_static_module_ctx = {
NULL, /* preconfiguration */
ngx_http_static_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_static_module = {
NGX_MODULE_V1,
&ngx_http_static_module_ctx, /* module context */
NULL, /* module directives */
NGX_HTTP_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
};
3. 文件查找实现
经过前面的模块注册到 NGX_HTTP_CONTENT_PHASE 中,就会被nginx调用。前提是没有其他更合适的处理器的时候。而因为 static_module 是在 NGX_HTTP_CONTENT_PHASE 中,所以都会走content的处理器:
// http/ngx_http_core_module.c
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);
// 处理成功,则返回ok
if (rc != NGX_DECLINED) {
ngx_http_finalize_request(r, rc);
return NGX_OK;
}
/* rc == NGX_DECLINED */
ph++;
if (ph->checker) {
r->phase_handler+&