挂载在同一阶段的Nginx模块的执行顺序

现在nginx的模块越来越丰富, 添加的handler模块可能挂载到同一个阶段(phrase), 一般情况下这个顺序不会那么重要, 但是总有例外的情况, 是吧?

为了找到答案, 先搜索了一下, 发现了Maxim Dounin这个回复. 简单看就是说在configure的时候, 越晚添加的模块就会越先执行(挂载在同一阶段的模块哈).

接下来准备自己动手尝试一下;)

尝试

最简单的验证方式: 写两个相同的模块, 挂载在相同的阶段, 执行阶段打印不同的错误日志, 之后比较输出顺序即可.

写一个很无聊的handler模块:

ngx_http_foo_module.c
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

static ngx_int_t ngx_http_foo_init(ngx_conf_t *cf);

static ngx_int_t ngx_http_foo_handler(ngx_http_request_t *r);

static ngx_http_module_t ngx_http_foo_module_ctx = {
    NULL,                              /* preconfiguration */
    ngx_http_foo_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_foo_module = {
    NGX_MODULE_V1,
    &ngx_http_foo_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
};

static ngx_int_t
ngx_http_foo_handler(ngx_http_request_t *r)
{
    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
            "#### hello, this FOO module");

    return NGX_DECLINED;
}

static ngx_int_t
ngx_http_foo_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);

    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    *h = ngx_http_foo_handler;

    return NGX_OK;
}

这个模块直接是在PREACCESS阶段打印一行错误日志. 我们可以直接复制整个foo代码, 替换foo关键字为bar, 这样我们就有两个同时挂载在PREACCESS的模块了.

接下来我们直接添加一下参数来编译

configure
1
--add-module=path/to/ngx-foo-module --add-module=path/to/ngx-bar-module

访问一下, 打印的错误日志像是这样子的:

error.log
1
2
xxxxxx #### hello, this BAR module
xxxxxx #### hello, this FOO module

看样子就是后添加的bar在先添加的foo模块之前运行.

原因

先看一下nginx编译目录下的objs/ngx_modules.c文件, 有这么一段:

ngx_modules.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ngx_module_t *ngx_modules[] = {
    &ngx_core_module,
    &ngx_errlog_module,
    &ngx_conf_module,
    &ngx_events_module,
    &ngx_event_core_module,

    ...

    &ngx_http_foo_module,
    &ngx_http_bar_module,

    ...
};

之前我觉得看到这个模块顺序, 会比较疑惑, 因为上面的模块代码在挂载的时候是通过push方式压入数组的, 按常理应该是从前往后依次执行各个阶段的handler. 好吧, 貌似需要看一下配置的解析的相关的代码了.

有关配置解析这部分代码强烈推荐淘宝雕粱同学的这篇文章, 很赞, 很详细. 这里就简单看看.

有关配置解析主要是在ngx_init_cycle函数中调用ngx_conf_parse来实现. 再此之前会初始化核心(core)模块, 例如http模块. 而现在写的两个模块是在解析http模块的基础上挂载的. 一个简单办法就是看一下模块调用postconfiguration的地方. 然后在http/ngx_http.c有这么一段代码:

http/ngx_http.c
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char                        *rv;
    ngx_uint_t                   mi, m, s;
    ngx_conf_t                   pcf;
    ngx_http_module_t           *module;
    ngx_http_conf_ctx_t         *ctx;
    ngx_http_core_loc_conf_t    *clcf;
    ngx_http_core_srv_conf_t   **cscfp;
    ngx_http_core_main_conf_t   *cmcf;

    /* the main http context */

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    *(ngx_http_conf_ctx_t **) conf = ctx;


    /* count the number of the http modules and set up their indices */

    ngx_http_max_module = 0;
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        ngx_modules[m]->ctx_index = ngx_http_max_module++;
    }

    ....

    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;

        if (module->postconfiguration) {
            if (module->postconfiguration(cf) != NGX_OK) {    //调用模块的init函数
                return NGX_CONF_ERROR;
            }
        }
    }

    ....

    if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {   //关键
        return NGX_CONF_ERROR;
    }

    ....
}

static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
    ngx_int_t                   j;
    ngx_uint_t                  i, n;
    ngx_uint_t                  find_config_index, use_rewrite, use_access;
    ngx_http_handler_pt        *h;
    ngx_http_phase_handler_t   *ph;
    ngx_http_phase_handler_pt   checker;

    cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
    cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
    find_config_index = 0;
    use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
    use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;

    n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        n += cmcf->phases[i].handlers.nelts;
    }

    ph = ngx_pcalloc(cf->pool,
                     n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
    if (ph == NULL) {
        return NGX_ERROR;
    }

    cmcf->phase_engine.handlers = ph;
    n = 0;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        h = cmcf->phases[i].handlers.elts;

        switch (i) {
        case NGX_HTTP_SERVER_REWRITE_PHASE:
            if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.server_rewrite_index = n;
            }
            checker = ngx_http_core_rewrite_phase;

            break;

        /* 其他的一些处理 */

        ...
        }

        n += cmcf->phases[i].handlers.nelts;

        /* 从后面开始... */
        for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) {
            ph->checker = checker;
            ph->handler = h[j];
            ph->next = n;
            ph++;
        }
    }

    return NGX_OK;
}

看到这边总算明白了一点;)

c, nginx

Comments