NGINX中HTTP请求的11个阶段

Nginx的HTTP框架基本由1个核心模块ngx_http_module、两个HTTP模块(ngx_http_core_modulenginx_http_upstream_module)组成,负责调用其他HTTP模块来一起处理用户请求。Nginx把HTTP请求处理流程划分为了11个阶段,其中每个处理阶段都可以由任意多个HTTP模块流水式地处理请求。

管理配置项相关的结构体

ngx_http_module

ngx_http_module是HTTP模块的核心模块,该模块的功能是定义新的HTTP模块类型,并为每个HTTP模块定义通用接口ngx_http_module_t结构体,管理HTTP模块生成的配置项结构体,并解析HTTP类配置项。

Nginx定义了一种基础类型的模块:核心模块,它的模块类型叫做NGX_CORE_MODULE, 在ngx_config_file.h中,

1
2
3
4
5
typedef struct {
ngx_str_t name;
void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;

其中create_conf回调方法来创建存储配置顼的数据结构,在读取nginx.conf配置文件时,会根据模块中的ngx_command_t把解析出的配置项存放在这个数据结构中;它还提供了init_conf回调方法,用于在解析完配置文件后,使用解析出的配置项初始化核心模块功能。

ngx_command_t定义在nginx_http.c中,解析HTTP模块的入口函数是ngx_http_block:

1
2
3
4
5
6
7
8
9
10
11
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
};

那么接下来看看ngx_http_module核心模块的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 定义核心模块的通用接口上下文结构 */
static ngx_core_module_t ngx_http_module_ctx = {
ngx_string("http"),
NULL,
NULL
};

/* 定义http核心模块 */
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
};

ngx_http_module_t

在Nginx中定义了HTTP模块的通用接口ngx_http_module_t结构体,该结构体定义在文件ngx_http_config.h,把直属于http{}server{}location{}块的配置项分别称为main、srv、loc级别配置项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
typedef struct {
/* 指向一个指针数组,数组中的每个成员都是由所有HTTP模块的create_main_conf方法创建的存放全局配置项的结构体,
它们存放着解析直属http{}块内的main级别的配置项参数 */
void **main_conf;

/* 指向一个指针数组,数组中的每个成员都是由所有HTTP模块的create_srv_conf方法创建的与server相关的结构体,它们或存放main级别配置项,
或存放srv级别的server配置项,这与当前的ngx_http_conf_ctx_t是在解析http{}或者server{}块时创建的有关 */
void **srv_conf;

/* 指向一个指针数组,数组中的每个成员都是由所有HTTP模块的create_loc_conf方法创建的与location相关的结构体,
它们可能存放着main、srv、loc级别的配置项,这与当前的ngx_http_conf_ctx_t是在解析http{}、server{}或者location{}块时
创建的有关存放location{}配置项*/
void **loc_conf;
} ngx_http_conf_ctx_t;

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;

11个阶段

HTTP模块在处理已经解析完毕的HTTP请求、获得并解析配置项后,将一个请求分为顺序性的多个处理阶段,前一个阶段的结果会影响后一个阶段的处理。一个请求在Nginx处理的示意图:

ngx_http_core_module.h中有11个阶段的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
typedef enum {
/* 在接收到完整的HTTP头部后处理的HTTP阶段,比如使用realip模块*/
NGX_HTTP_POST_READ_PHASE = 0,

/* 在将请求的URI与location表达式匹配前,修改请求的URI(所谓的重定向)是一个独立的HTTP阶段 */
NGX_HTTP_SERVER_REWRITE_PHASE,

/* 根据请求的URL寻找匹配的location表达式,这个阶段只能由ngx_http_core_module模块实现,
不建议其他HTTP模块模块重新定义这一阶段的行为 */
NGX_HTTP_FIND_CONFIG_PHASE,

/* 在NGX_HTTP_FIND_CONFIG_PHASE阶段寻找到匹配的location之后再修改请求的URI */
NGX_HTTP_REWRITE_PHASE,

/* 这一阶段用于在rewrite重写URL后,防止错误的nginx配置导致死循环(递归地修改URI),因此,
这一阶段仅由ngx_http_core_module模块处理。目前,控制死循环的方法就是看rewrite次数,
超过一定阈值就认为出现了死循环,返回500 */
NGX_HTTP_POST_REWRITE_PHASE,

/* 表示在处理NGX_HTTP_ACCESS_PHASE阶段决定请求的访问权限前,HTTP模块可以介入的处理阶段,比如limit_conn或limit_req */
NGX_HTTP_PREACCESS_PHASE,

/* 这个阶段用于让HTTP模块判断是否允许这个请求访问Nginx服务器, 比如auth_basic, access, auth_request*/
NGX_HTTP_ACCESS_PHASE,

/* 在NGX_HTTP_ACCESS_PHASE阶段中,当HTTP模块的handler处理函数返回不允许访问的错误码时(实际
就是NGX_HTTP_FORBIDDEN或者NGX_HTTP_UNAUTHORIZED),这里将负责向用户发送拒绝服务的错误响应,
因此这个阶段实际上用于给NGX_HTTP_ACCESS_PHASE阶段收尾 */
NGX_HTTP_POST_ACCESS_PHASE,

/* 这个阶段完全为try_files配置项而设立的,当HTTP访问静态文件资源时,try_files配置项可以使这个
请求顺序地访问多个静态文件资源,如果某一次访问失败,则继续访问try_files中指定的下一个静态资源 */
NGX_HTTP_PRECONTENT_PHASE,

/* 用于处理HTTP请求内容的阶段,这是大部分HTTP模块最愿意介入的阶段,比如index, autoindex */
NGX_HTTP_CONTENT_PHASE,

/* 处理完请求记录日志的阶段。例如,ngx_http_log_module模块就在这个阶段中加入了一个handler处理方法,使得每个HTTP请求处理完毕后会记录access_log日志 */
NGX_HTTP_LOG_PHASE
} ngx_http_phases;

目前只是简单看了一下这几处的源码,后面再安排仔细读一下好了😄

参考

1、《深入理解Nginx(第2版)》
2、《Nginx核心知识100讲》

0%