您的位置:首页 > 运维架构 > Nginx

Nginx学习-编写一个Helloworld模块(1)

2014-04-01 21:18 387 查看

    学习Nginx如果直接看代码那肯定会疯掉的,幸好有许多前辈的文章和书籍参考,和在某高人指点下,我开始从一个简单的HTTP模块入手,开始我的学习之旅。

    网上有很多Nginx的Helloworld模块编写教程,我也是参考了其中一个,先实现了一遍,这里贴一下我看的教程https://www.geek-share.com/detail/2549698880.html,实现之后我也有诸多疑问(完全搞不懂在干什么,反正是实现了...),现在就结合他写的代码和我最近的学习成果,分析一下这些代码究竟干了什么,和Nginx是怎么联系起来的。

    首先,要写http模块就得先取个名字,教程中的模块名称为ngx_http_hello_world_module,与此相对应的数据结构是

ngx_module_t ngx_http_hello_world_module = {
    NGX_MODULE_V1,
    &ngx_http_hello_world_module_ctx,
    ngx_http_hello_world_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,        
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};

先看一下ngx_module_t的定义

typedef struct ngx_module_s ngx_module_t
struct ngx_module_s {
    ngx_uint_t            ctx_index;
    ngx_uint_t            index;
    ngx_uint_t            spare0;
    ngx_uint_t            spare1;
    ngx_uint_t            spare2;
    ngx_uint_t            spare3;
    ngx_uint_t            version;
    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);
    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_http_hello_world_module中先对通过NGX_MODULE_V1宏对前7个参数做了初始化

#NGX_MODULE_V1  0,0,0,0,0,0,1

接着将*ctx和*commands分别指向另外定义的2个数据结构,将type定义为NGX_HTTP_MODULE,之后将7个回调函数赋值为NULL,再用NGX_MODULE_V1_PADDING宏对最后8各参数做了初始化

#NGX_MODULE_V1_PADDING  0,0,0,0,0,0,0,0

这里我们需要关心的是*ctx和*commands这2个指针和其对应的数据结构

*ctx:用于指向模块的上下文结构体,由于Nginx中不同模块的功能都各不相同,所以设计为void*类型,在HTTP模块中需要指向ngx_http_module_t结构体。

*commands:用于处理nginx.conf中的配置项。

PS:看到这里可以发现,只要Nginx能够访问到ngx_http_hello_world_module,就能访问到另外2个数据结构了。

现在来看*ctx指向的ngx_http_hello_world_module_ctx

static ngx_http_module_t ngx_http_hello_world_module_ctx = {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ngx_http_hello_world_create_loc_conf,
    ngx_http_hello_world_merge_loc_conf
};

ngx_http_module_t的定义

typedef struct ngx_http_module_s ngx_http_module_t
struct ngx_http_module_s{
    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_hello_world_module_ctx中只指向了我们定义的2个函数,其中ngx_http_hello_world_create_loc_conf用于创建存储配置项的数据结构,并将该结构的地址返回给Nginx框架,在这个函数中创建了我们为模块自定义的数据结构,

typedef struct {
    ngx_str_t output_words;
} ngx_http_hello_world_loc_conf_t;

而ngx_http_hello_world_merge_loc_conf用于合并srv和loc级别下的同名配置项(意思就是不同级别的配置项中出现了名字相同的配置项以哪个为准,具体标准由模块实现者决定)。这个结构中的函数在NginxHTTP框架启动过程中自动调用。

接着来看*commands指向的ngx_http_hello_world_commands

static ngx_command_t ngx_http_hello_world_commands[] = {
    {
        ngx_string("hello_world"), // The command name
        NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
        ngx_http_hello_world, // The command handler
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_hello_world_loc_conf_t, output_words),
        NULL
    },
    ngx_null_command
};

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;
};

name:指定了我们感兴趣的配置项的名称,其中ngx_string是一个宏,用于将字符串转换为ngx_str_t类型数据。

type:表示配置项可以出现的位置和接受参数的个数,set方法用于处理配置项的参数。

conf:用于表示用何级别的方法生成的结构体来保存这个参数。

offset:表示这个配置项在所对应的参数再结构中的偏移位置(offsetof可以直接给出结构体中数据项的位置)。

*post:这个用途就有很多了,这里就不讨论了。

看到这里可以发现只要nginx.conf文件中出现了hello_world配置项就会调用ngx_http_hello_world对配置项进行处理。

我们发现我们定义的ngx_http_hello_world_commands的结构中有2个ngx_command_t结构(ngx_null_command为一个宏,定义一个空的ngx_command_t),这是因为我们的配置项可能有很多,ngx_null_command表示不再需要解析配置项了(我的理解)。

这里我们已经将该模块的几个主要的数据结构分析了一遍,可能还不够深刻,有空再把文中提到的数据结构详细整理一遍。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: