Nginx 配置项参数解析

来源:互联网 发布:淘宝导出库存 编辑:程序博客网 时间:2024/06/01 22:43

在简单的http模块中mytest配置项后面没有跟任何参数。本篇博客学习讨论HTTP模块是怎样获得感兴趣的配置项。

处理http配置项可以分为下面4个步骤:
(1)创建数据结构用于存储配置项对应的参数。
(2)设定配置项在nginx.conf中出现时的限制条件与回调方法。
(3)实现第二步中的回调方法,或者使用Nginx框架预设的14个回调方法。
(4)合并不同级别的配置块中出现的同名配置项。

这四个步骤是通过ngx_http_module_t以及ngx_command_t有机的结合起来。

本例中将在nginx.conf中添加一些配置项来自己编写模块进行解析。

worker_processes  1;events {    worker_connections  1024;}http {    include       mime.types;    default_type  application/octet-stream;    sendfile        on;    keepalive_timeout  65;    #gzip  on;    server {        listen       80;        server_name  localhost;    location = / {        root   html;        index  index.html index.htm;    }    location  = /zxtest{        test_str "zhangxiaoha";        test_num 23;        test_flag off;        test_size 10k;        mytest;    }    error_page   500 502 503 504  /50x.html;    location = /50x.html {        root   html;    }}

我们希望通过自己模块将配置项中的参数保存到数据结构中,并通过访问localhost/zxtest将内容显示到浏览器上。


1.数据结构

用一个结构体来包含所有我们感兴趣的参数,上述配置文件中,有4个带配置项值的配置项,因此我们的结构体与之对应:

typedef struct{    ngx_str_t my_str;    ngx_int_t my_num;    ngx_flag_t my_flag;    size_t my_size;}ngx_http_mytest_conf_t;

在陶辉老师的《深入理解Nginx》一书中为了说明14种预设配置项的解析方法,因此在结构体中包含了14个成员,我为了简单起见,就用了4个。


2.预设的配置项解析方法

配置项解析方法通过ngx_command_t中的char *(*set)(ngx_conf_t *cf,ngx_command_t*cmd,void *conf)回调成员设置,关于ngx_command_t结构的详细声明见core/ngx_conf_file.h

Nginx有14种预设的配置项解析方式:

(1)ngx_conf_set_flag_slot

配置项的参数是on或者off,当为on时结构体中对应变量被设置为1,否则被设置为0。

本例中,我们希望在nginx.conf中有一个配置项的名称为test_flag,并且它后面携带的参数是on或者off,
那么我们可以使用生成的ngx_http_mytest_conf_t结构体中的以下成员保存:

ngx_flag_t my_flag;

test_flag配置项的值为on时,结构体中my_flag的值将设置为1

(2)ngx_conf_set_str_slot

用来设置ngx_str_t类型的变量。

(3)ngx_conf_set_str_array_slot

该方法会将同名配置项的参数以ngx_str_t的类型存放到ngx_array_t队列容器中
例如nginx.conf有如下配置:

location ...{    test_str_array Content-Length;    test_str_array Content-Encoding;}

my_str_array->nelts的值是2,表示出现了两个test_str_array配置项。
并且,my_str_array->elts指向ngx_str_t类型组成的数组。
因此,我们进行如下访问:

ngx_str_t*pstr = mycf->my_str_array->elts;//pstr[0].len=14//pstr[0].data="Content-Length"//pstr[1].len=16//pstr[1].data="Content-Encoding"

(4)ngx_conf_set_keyval_slot

ngx_conf_set_str_array_slot类似,只不过其数组元素是一个Key/Value类型,
因此,test_keyval配置项的后面需要跟两个参数,并且在command中的type参数指定为
NGX_CONF_TAKE2

这种key-value的结构体如下定义:

// ~/nginx/src/core/ngx_string.htypedef struct{    ngx_str_t key;    ngx_str_t value;} ngx_keyval_t;

(5) ngx_conf_set_num_slot

处理带一个参数的配置项。
如果使用test_num表示这个配置项名称,在nginx.conf中有如下:

location ...{    test_num 10}

则在结构体中ngx_int_t my_num成员将会变成10

(6) ngx_conf_set_size_slot

配置项希望表达的含义是空间的大小,那么该回调函数就十分适合。
用结构体ngx_http_mytest_conf_t中的size_t my_size来存储参数。
如果配置了test_size 10k;,则该参数将被设置成10240

(7)ngx_conf_set_off_slot

配置项表达的含义是空间的偏移位置,则使用该内置回调函数。
结果最终将保存在ngx_http_mytest_conf_toff_t my_off成员中

(8)ngx_conf_set_msec_slot

解析之后将以毫秒的形式存储在ngx_http_mytest_conf_t中的
ngx_msec_t my_msec;成员中。
例如test_msec 1d,将设置86400000,表示一天的毫秒数。

(9)ngx_conf_set_sec_slot

跟上述类似,单位是秒。

(10)ngx_conf_set_bufs_slot

配置项需要携带两个参数,第一个参数表示缓存区的个数,
第二个参数表示缓存区的大小。

用来保存这两个参数的是一个结构体变量:

// nginx/src/core/ngx_buf.htypedef struct{    ngx_int_t num;    size_t size;}ngx_bufs_t;//区别 ngx_buf_t

(11)ngx_conf_set_enum_slot

nginx的enum类型定义如下:

//nginx/src/core/ngx_conf_file.htypedef struct {    ngx_str_t name;    ngx_uint_t value;}ngx_conf_enum_t;

事先定义一个ngx_conf_enum_t类型的数组,作用类似于字典,例如:

static ngx_conf_enum_t test_enums[]={    {ngx_string("apple"),1},    ...    ...    {ngx_null_string,0}}

nginx.conf中配置项test_enum的参数必须是上述数组中出现的,
否则,Nginx将会报错。而保存enum类型的是一个ngx_uint_t类型的变量。

(12)ngx_conf_set_bitmask_slot

ngx_conf_enum_t类似,需要事先定义一个ngx_conf_bitmask_t类型的数组。

// nginx/core/src/ngx_conf_file.h typedef struct{    ngx_str_t name;    ngx_uint_t mask;}ngx_conf_bitmask_t;

(13)ngx_conf_set_access_slot

用来设置读写权限。

(14)ngx_conf_set_path_slot

用来设置路径


3.创建与合并数据结构

ngx_http_module_t结构体中定义了8个回调函数(详见http/ngx_http_config.h),其中:

typedef struct {//...//当需要创建数据结构用于存储main级别的全局配置项时,可以通过create_main_conf回调方法创建存储全局配置项的结构体    //(直属于http{}块的配置项)    void       *(*create_main_conf)(ngx_conf_t *cf);    //初始化main级别配置项    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);    //创建存储srv级别配置项的结构体(server块)    void       *(*create_srv_conf)(ngx_conf_t *cf);    //合并main级别和srv级别下的同名配置项    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);    //创建用于存储loc级别(直属于location{})的配置项    void       *(*create_loc_conf)(ngx_conf_t *cf);    //合并srv级别和location级别下的同名配置项    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);}ngx_http_module_t;

有三组创建与合并的函数,目的是处理不同配置块中(http{} server{} location{})的配置项。

以loc级别的一组创建与合并的函数为例,create_loc_conf回调函数用来主要为我们定义的结构体分配空间ngx_pcalloc
merge_loc_conf回调函数用来合并server{}配置块(相对于loc来说是parent)中的相同配置项的参数,nginx也提供相关的函数来指导怎样合并(例如以同一配置项是上一级别的参数为准,还是本级别的参数为准)。


4.自定义配置项处理函数

现在我们要自定义配置项处理方法,将我们从配置项中读出的参数输出到浏览器中(text/html)。
实际上,在Nginx开发简单的HTTP模块就使用了自定义的配置项处理方法,在《深入理解Nginx》中,作者自定义postconfiguration回调在终端打印配置项参数信息。

这里我在自定义配置项处理的回调函数中ngx_conf_http_mytest设置真正的处理工作的ngx_http_mytest_handler,发送http响应。


5.完整代码

config文件

ngx_addon_name=ngx_http_mytest_moduleHTTP_MODULES="$HTTP_MODULES ngx_http_mytest_module"NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mytest_module.c"

ngx_http_mytest_module.c

//ngx_http_mytest_module.c#include <ngx_config.h>#include <ngx_core.h>#include <ngx_http.h>static ngx_int_tngx_http_mytest_handler(ngx_http_request_t *r);static char *ngx_conf_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);  static void*  ngx_http_mytest_create_loc_conf(ngx_conf_t *cf);static char*  ngx_http_mytest_merge_loc_conf(ngx_conf_t *cf,void *parent,void *child); //保存参数的数据结构typedef struct{    ngx_str_t my_str;    ngx_int_t my_num;    ngx_flag_t my_flag;    size_t my_size;}ngx_http_mytest_conf_t;//合并loc级别的配置项参数static char*  ngx_http_mytest_merge_loc_conf(ngx_conf_t *cf,void *parent,void *child){      ngx_http_mytest_conf_t *prev = parent;      ngx_http_mytest_conf_t *conf = child;      ngx_conf_merge_str_value(conf->my_str,prev->my_str,"defaultstr");      return NGX_CONF_OK;  }//创建loc级别的配置项参数static void*  ngx_http_mytest_create_loc_conf(ngx_conf_t *cf){      ngx_http_mytest_conf_t *conf;      conf = ngx_pcalloc(cf->pool,sizeof(ngx_http_mytest_conf_t));      if(NULL == conf){          return NGX_CONF_ERROR;      }      conf->my_str.len = 0;      conf->my_str.data = NULL;      conf->my_flag = NGX_CONF_UNSET;      conf->my_num = NGX_CONF_UNSET;      conf->my_size = NGX_CONF_UNSET_SIZE;      return conf;  }  //http模块山下文static ngx_http_module_tngx_http_mytest_module_ctx={    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    ngx_http_mytest_create_loc_conf,    ngx_http_mytest_merge_loc_conf};//定义模块的配置文件参数static ngx_command_tngx_http_mytest_commands[]={    {        ngx_string("test_flag"),        NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,        ngx_conf_set_flag_slot,        NGX_HTTP_LOC_CONF_OFFSET,        offsetof(ngx_http_mytest_conf_t,my_flag),        NULL    },    {        ngx_string("test_str"),        //携带一个参数且可以出现在http/server/location配置块        NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,        //nginx预设的方法        ngx_conf_set_str_slot,        NGX_HTTP_LOC_CONF_OFFSET,        offsetof(ngx_http_mytest_conf_t, my_str),        NULL    },    {        ngx_string("test_num"),        NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,        ngx_conf_set_num_slot,        NGX_HTTP_LOC_CONF_OFFSET,        offsetof(ngx_http_mytest_conf_t, my_num),        NULL    },    {        ngx_string("test_size"),        NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,        ngx_conf_set_size_slot,        NGX_HTTP_LOC_CONF_OFFSET,        offsetof(ngx_http_mytest_conf_t, my_size),        NULL    },    {        ngx_string("mytest"),        NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,        ngx_conf_http_mytest,        NGX_HTTP_LOC_CONF_OFFSET,        0,        NULL    },    ngx_null_command};//nginx模块定义ngx_module_t ngx_http_mytest_module={    NGX_MODULE_V1,    &ngx_http_mytest_module_ctx,    ngx_http_mytest_commands,    NGX_HTTP_MODULE,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NGX_MODULE_V1_PADDING};//自定义配置项处理函数    static char*ngx_conf_http_mytest(ngx_conf_t *cf,    ngx_command_t *cmd,    void*conf){    ngx_http_core_loc_conf_t *clcf;    clcf=ngx_http_conf_get_module_loc_conf(cf,ngx_http_core_module);//handler是实际处理    clcf->handler=ngx_http_mytest_handler;    return NGX_CONF_OK;}//handler回调    static ngx_int_tngx_http_mytest_handler(ngx_http_request_t *r){    //存储配置项参数    ngx_http_mytest_conf_t *elcf;    elcf=ngx_http_get_module_loc_conf(r,ngx_http_mytest_module);    if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD |NGX_HTTP_POST))) {          return NGX_HTTP_NOT_ALLOWED;      }      //丢弃报文    ngx_int_t rc=ngx_http_discard_request_body(r);    if(NGX_OK!=rc){        return rc;    }    //这里使用"text/html"如果使用"text/plain"浏览器会将下载文本当做bin文件下载下来    ngx_str_t type=ngx_string("text/html");    ngx_str_t format=ngx_string("helloworld,test_str=%v,test_flag=%i,test_num=%i,test_size=%z");    ngx_str_t test_str_arg=elcf->my_str;    ngx_int_t test_num_arg=elcf->my_num;    ngx_flag_t test_flag_arg=elcf->my_flag;    size_t test_size_arg=elcf->my_size;    int data_len=format.len+test_str_arg.len+1;    r->headers_out.status=NGX_HTTP_OK;    r->headers_out.content_length_n = data_len;//响应包包体内容长度      r->headers_out.content_type = type;      rc=ngx_http_send_header(r);    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {          return rc;      }      ngx_buf_t *b;      b = ngx_create_temp_buf(r->pool,data_len);      if (b == NULL){      return NGX_HTTP_INTERNAL_SERVER_ERROR;      }      ngx_snprintf(b->pos,data_len,(char *)format.data,&test_str_arg,test_flag_arg,test_num_arg,test_size_arg);      b->last = b->pos + data_len;      b->last_buf = 1;     ngx_chain_t out;      out.buf = b;      out.next = NULL;      return ngx_http_output_filter(r, &out);  //发送响应报文}

6.测试

将模块编入nginx,重启nginx,在浏览器输入特定url,效果如下:
<

这里写图片描述


7.参考

1.《深入理解Nginx》(第二版)
2.http://blog.csdn.net/xiajun07061225/article/details/9147265

1 0
原创粉丝点击