Nginx之所以有如此多的特性,是因为有大量的开发者在为其开发第三方模块,Nginx的模块化设计架构非常优良,那么究竟要如何表示一个模块?
1. Nginx模块是怎么定义的:
Nginx对模块做了高度抽象,每一个模块都用一个 ngx_module_t
结构体来表示:
typedef struct ngx_module_s ngx_module_t;
struct ngx_module_s {
ngx_uint_t ctx_index; //本模块在同类型模块中的序号(如在http类型或event类型中的序号,默认为-1)
ngx_uint_t index; //本模块在所有模块中的序号。如果某个模块跟其他模块的功能是冲突的, 那么先生效的模块会阻碍后生效的模块发挥作用
char *name; //模块的名字,字符串,默认为空指针
ngx_uint_t spare0; //保留字段
ngx_uint_t spare1; //保留字段
ngx_uint_t version; //nginx.h: #define nginx_version 1010000
const char *signature; //模块的二进制兼容性签名:NGX_MODULE_SIGNATURE
//以上以几个变量可以使用 NGX_MODULE_V1初始化:
//#define NGX_MODULE_V1 0,0,0,0,0,0,1
void *ctx; //指向细分的子模块类型的结构体,如 &ngx_http_module_t, &ngx_event_module_t, &ngx_core_module_t 等
ngx_command_t *commands; //模块所支持的指令,数组形式
ngx_uint_t type; //本模块所属子模块的类型标识,如 NGX_HTTP_MODULE, NGX_EVENT_MODULE, NGX_CORE_MODULE, NGX_CONF_MODULE, NGX_MAIL_MODULE, NGX_STREAM_MODULE,目前共有6种
//以下7个函数会在进程的启动或者结束阶段被调用,也就是说任何一个第三方模块都有机会在master进程 或 worker进程 的启动 或 退出时加载自己的代码:
ngx_int_t (*init_master)(ngx_log_t *log); //目前Nginx不会调用 init_master
ngx_int_t (*init_module)(ngx_cycle_t *cycle); //在master进程fork出worker进程之前被调用,做一些基本的初始化工作,数据会被子进程复制
ngx_int_t (*init_process)(ngx_cycle_t *cycle); //在worker进程进入工作循环之前被调用,初始化每个子进程自己专用的数据
ngx_int_t (*init_thread)(ngx_cycle_t *cycle); //Nginx目前不支持线程,所以init_thread目前不会被调用
ngx_int_t (*exit_thread)(ngx_cycle_t *cycle); //nginx目前不支持线程,所以exit_thread目前不会被调用
ngx_int_t (*exit_process)(ngx_cycle_t *cycle); //worker进程退出前调用
ngx_int_t (*exit_master)(ngx_cycle_t *cycle); //master进程退出前调用
//以下8个成员目前Nginx不会调用,使用 NGX_MODULE_V1_PADDING 填充
uintptr_t spare_hook0;
uintptr_t spare_hook1;
uintptr_t spare_hook2;
uintptr_t spare_hook3;
uintptr_t spare_hook4;
uintptr_t spare_hook5;
uintptr_t spare_hook6;
uintptr_t spare_hook7;
};
其中,ngx_command_t
结构体用于定义 Nginx 指令:
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; //专门给 http/stream 模块使用,决定存储在 main/server/loaction 哪个层次,其他模块不使用
ngx_uint_t offset; //主要用于ngxin内置的命令解析函数,自定义的命令解析函数可以置为0。用于表示变量在conf结构体中的偏移量
void *post; //解析后处理的数据
};
#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL }
//空指令,用于在指令数组的最后当做哨兵,结束数组,避免ngx_command_t[] 指定长度。类似于'\0'的结束字符串的作用
2. 定义一个Nginx第三方模块举例:
//ngx_http_pagecount_module.c
//定义一个 ngx_http_count_module 模块:
#include <ngx_http.h>
#include <ngx_config.h>
#include <ngx_core.h>
static char *ngx_http_count_module_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
//do something ...
//ngx_http_count_module_set作为 "count" 指令的回调解析函数
}
static ngx_http_module_t count_ctx {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
static ngx_command_t count_commands[] = {
{
ngx_string("count"),
NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
ngx_http_count_module_set,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL
},
// {
// ngx_string("another_command"),
// ...
// },
ngx_null_command
};
ngx_module_t ngx_http_count_module {
NGX_MODULE_V1,
&count_ctx,
count_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};
对应的配置文件可写成:
http {
server {
location /count {
count; #我们所定义的第三方模块中提供的指令
}
}
}