学习Nginx如果直接看代码那肯定会疯掉的,幸好有许多前辈的文章和书籍参考,和在某高人指点下,我开始从一个简单的HTTP模块入手,开始我的学习之旅。
网上有很多Nginx的Helloworld模块编写教程,我也是参考了其中一个,先实现了一遍,这里贴一下我看的教程,实现之后我也有诸多疑问(完全搞不懂在干什么,反正是实现了...),现在就结合他写的代码和我最近的学习成果,分析一下这些代码究竟干了什么,和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_tstruct 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_tstruct 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表示不再需要解析配置项了(我的理解)。
这里我们已经将该模块的几个主要的数据结构分析了一遍,可能还不够深刻,有空再把文中提到的数据结构详细整理一遍。