整个Nginx的生命周期大概分为:
- 配置文件读取并解析阶段
- 具体请求处理阶段
我们所有的工作都在读取配置文件并解析阶段完成,在具体的请求处理阶段,一般会回调提前注册好的一系列回调函数。
总体来说,Nginx模块开发就是在配置文件读取并解析阶段,设置一系列变量以及回调函数。
每个Nginx模块都是 ngx_module_t 结构体变量,其中存储了模块的信息,以及一系列的回调函数。
我们开发Nginx模块,实际上就是编写一个 ngx_module_t 结构体变量。
typedef struct ngx_module_s ngx_module_t;
struct ngx_module_s {
/* private part is omitted */
void *ctx;
ngx_command_t *commands;
ngx_uint_t type;
ngx_int_t (*init_master)(ngx_log_t *log);
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
void (*exit_thread)(ngx_cycle_t *cycle);
void (*exit_process)(ngx_cycle_t *cycle);
void (*exit_master)(ngx_cycle_t *cycle);
/* stubs for future extensions are omitted */
};
可以看到其中大部分类型都是简单类型,要么是int类型,要么是函数指针类型。 重点需要关注其中的两个部分:ctx,commands。
首先看一下 commands ,可以看到其类型为 ngx_command_t*,因此应该传入一个 ngx_command_t 数组。
typedef struct ngx_command_s ngx_command_t;
struct ngx_command_s {
ngx_str_t name;
ngx_uint_t type;
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_uint_t conf;
ngx_uint_t offset;
void *post;
};
ngx_command_t 类型中定义了每个命令的相关信息,包括名称,类型,set 回调函数等等
接下来看一下 ctx ,可以看到其类型为 void*,因此这个变量的类型是不确定的。也就是说对于不同类型的模块拥有不同类型的 ctx。从 官方文档 中可以看到,模块类型分为以下几种。
NGX_CORE_MODULENGX_EVENT_MODULENGX_HTTP_MODULENGX_MAIL_MODULENGX_STREAM_MODULE
这里列出了常用的两种模块类型 NGX_CORE_MODULE,NGX_HTTP_MODULE 的 ctx 类型定义。
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;
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;
可以看到不管是 ngx_core_module_t 类型,还是 ngx_http_module_t 类型,本质上只是定义了一系列模块创建相关回调函数。
由此模块定义部分分析完毕。总体来看,编写模块就是编写一个 ngx_module_t 结构体,只需要按照其各部分的定义,将结构体填写完整即可。重点需要填写其中的ctx,commands两部分。