nginx 的一大设计亮点就是可拓展, 可插拔的模块化架构; 在使用的时候, 我们可以根据自己业务的需求选择合适的模块组合;
本文涉及的源码基于 nginx 1.11.2.4;

nginx 模块相关数据结构

各数据结构的别名

1
2
3
4
5
/* core/ngx_core.h */
typedef struct ngx_module_s ngx_module_t;
typedef struct ngx_command_s ngx_command_t;
typedef struct ngx_event_s ngx_event_t;
typedef struct ngx_conf_s ngx_conf_t;

指令数据结构 ngx_command_t

用于定义模块拥有什么可以设置的 关键字/指令 (directive), 如 ngx_http_core_module 中的 user, pid, master_process 等;

1
2
3
4
5
6
7
8
9
/* core/ngx_conf_file.h */
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;
};

其中, 最重要的是第二个参数 type, 它是对以下两类 条件限定 作了一个 按位或 操作:

  1. 指令允许在哪些 context 中使用;
  2. 指令所接收的参数情况(参数形态, 参数个数);

对第一类条件, 限定的 context 上下文, 常用的类型如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 主模块/全局相关: core/ngx_conf_file.h */
#define NGX_MAIN_CONF 0x01000000 // 允许在 http, mail, events, error_log 等主模块下使用
#define NGX_ANY_CONF 0x1F000000 // 允许在配置文件中最外层, 与 http, mail, events, error_log 等同级

/* http 模块相关: http/ngx_http_config.h */
#define NGX_HTTP_MAIN_CONF 0x02000000 // 允许在 http 主模块指令下使用
#define NGX_HTTP_SRV_CONF 0x04000000 // 允许在 server 指令下使用
#define NGX_HTTP_LOC_CONF 0x08000000 // 允许在 location 指令下使用
#define NGX_HTTP_UPS_CONF 0x10000000 // 允许在 upstream 指令下使用
#define NGX_HTTP_SIF_CONF 0x20000000 // 允许在 server 指令域的 if 配置下使用
#define NGX_HTTP_LIF_CONF 0x40000000 // 允许在 location 指令域的 if 配置下
#define NGX_HTTP_LMT_CONF 0x80000000 // 允许在 limit 指令下使用

/* event 模块相关: event/ngx_event.h */
#define NGX_EVENT_CONF 0x02000000 // 允许在 events 指令下使用

对第二类条件, 首先是参数形态, 可以有如下类型:

1
2
3
#define NGX_CONF_BLOCK          0x00000100      // 允许参数是一个信息块, 即 { }, 内部嵌套其他指令
#define NGX_CONF_FLAG 0x00000200 // 允许参数类型是 on 或 off
#define NGX_CONF_ANY 0x00000400 // 允许参数是任意类型

然后是参数数目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 严格的参数数目
#define NGX_CONF_NOARGS 0x00000001
#define NGX_CONF_TAKE1 0x00000002
#define NGX_CONF_TAKE2 0x00000004
#define NGX_CONF_TAKE3 0x00000008
#define NGX_CONF_TAKE4 0x00000010
#define NGX_CONF_TAKE5 0x00000020
#define NGX_CONF_TAKE6 0x00000040
#define NGX_CONF_TAKE7 0x00000080
// 限定参数数目的下限
#define NGX_CONF_1MORE 0x00000800
#define NGX_CONF_2MORE 0x00001000
// 几种数目的组合
#define NGX_CONF_TAKE12 (NGX_CONF_TAKE1|NGX_CONF_TAKE2)
#define NGX_CONF_TAKE13 (NGX_CONF_TAKE1|NGX_CONF_TAKE3)
#define NGX_CONF_TAKE23 (NGX_CONF_TAKE2|NGX_CONF_TAKE3)
#define NGX_CONF_TAKE123 (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3)
#define NGX_CONF_TAKE1234 (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3|NGX_CONF_TAKE4)
// 最大的参数数目限制
#define NGX_CONF_MAX_ARGS 8

上下文数据结构 ngx_module_ctx

大致分为以下几类: core, http, event, mail;
ngx_conf_module 没有对应的 module context;

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
/* core/ngx_module.h */
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;

/* event/ngx_event.h */
typedef struct {
ngx_str_t *name;
void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
ngx_event_actions_t actions;
} ngx_event_module_t;

/* http/ngx_http_config.h */
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;

/* mail/ngx_mail.h */
typedef struct {
ngx_mail_protocol_t *protocol;
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);
} ngx_mail_module_t;

模块的类型

模块的类型都被定义为 无符号整型:

1
2
3
4
5
6
7
8
9
10
11
12
/* http/ngx_http_config.h */
#define NGX_HTTP_MODULE 0x50545448 /* "HTTP" */

/* core/ngx_conf_file.h */
#define NGX_CORE_MODULE 0x45524F43 /* "CORE" */
#define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */

/* mail/ngx_mail.h */
#define NGX_MAIL_MODULE 0x4C49414D /* "MAIL" */

/* event/ngx_event.h */
#define NGX_EVENT_MODULE 0x544E5645 /* "EVNT" */

模块定义数据结构 ngx_module_t

ngx_module_t 是 nginx module 的最终定义之处, 其中字段包括了上面提及的 ngx_module_ctx 上下文, ngx_command_t[] 可配置指令集, 模块的类型, 以及各种生命周期中的回调函数;

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
/* core/ngx_module.h */ 
struct ngx_module_s {
// 以下字段常使用宏定义 NGX_MODULE_V1
ngx_uint_t ctx_index;
ngx_uint_t index;
char *name;
ngx_uint_t spare0;
ngx_uint_t spare1;
ngx_uint_t version;
const char *signature;

// 需要自己定义的部分 (前 3 个必选, 后 7 个可为 NULL)
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);

// 以下字段常使用宏定义 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;
};

常用于填充的两个宏定义如下:

1
2
3
4
5
6
/*
* 申明 ngx_module_t 时常用的的宏定义
* core/ngx_module.h
*/
#define NGX_MODULE_V1 NGX_MODULE_UNSET_INDEX, NGX_MODULE_UNSET_INDEX, NULL, 0, 0, nginx_version, NGX_MODULE_SIGNATURE
#define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0

nginx 模块加载与查看

模块加载的时机

nginx 模块是在静态编译时加载的, 无法如 apache 一般使用动态链接库加载, 编译时指定选项以加载指定模块:

1
2
sudo /usr/local/nginx-1.11.2/configure --without-xxx_module --with-yyy_module
sudo make && sudo make install

nginx 编译完成后, 在 objs/ 目录下会自动生成模块加载的源文件 ngx_modules.c 与目标文件 ngx_modules.o:

1
2
3
4
5
6
7
8
9
10
11
12
13
ngx_module_t *ngx_modules[] = {
&ngx_core_module,
&ngx_errlog_module,
&ngx_conf_module,
&ngx_openssl_module,
&ngx_regex_module,
&ngx_events_module,
&ngx_event_core_module,
&ngx_epoll_module,
&ngx_http_module,
&ngx_http_core_module,
...
};

nginx 已加载模块的查看

1
2
3
4
5
6
> /usr/local/nginx/sbin/nginx -V
nginx version: openresty/1.11.2.4
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabled
configure arguments: --prefix=/usr/local/openresty/nginx --with-cc-opt=-O2 --add-module=../ngx_devel_kit-0.3.0 --add-module=../echo-nginx-module-0.60 --add-module=../xss-nginx-module-0.05 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.31 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.06 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.8 --add-module=../ngx_lua_upstream-0.06 --add-module=../headers-more-nginx-module-0.32 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.18 --add-module=../redis2-nginx-module-0.14 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.14 --add-module=../rds-csv-nginx-module-0.07 --with-ld-opt=-Wl,-rpath,/usr/local/openresty/luajit/lib --with-http_gunzip_module --with-http_gzip_static_module --with-http_ssl_module

参考链接