Nginx 模块开发之模块开发本质

155 阅读2分钟

整个Nginx的生命周期大概分为:

  1. 配置文件读取并解析阶段
  2. 具体请求处理阶段

我们所有的工作都在读取配置文件并解析阶段完成,在具体的请求处理阶段,一般会回调提前注册好的一系列回调函数。

总体来说,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类型,要么是函数指针类型。 重点需要关注其中的两个部分:ctxcommands

首先看一下 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_MODULE
  • NGX_EVENT_MODULE
  • NGX_HTTP_MODULE
  • NGX_MAIL_MODULE
  • NGX_STREAM_MODULE

这里列出了常用的两种模块类型 NGX_CORE_MODULENGX_HTTP_MODULEctx 类型定义。

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 结构体,只需要按照其各部分的定义,将结构体填写完整即可。重点需要填写其中的ctxcommands两部分。