天天看點

Nginx學習之二-配置項解析及程式設計實作

在開發功能靈活的Nginx子產品時,需要從配置檔案中擷取特定的資訊。不過,我們并不需要再編寫一套讀取配置的系統,Nginx已經為使用者提供了強大的配置項解析機制,同時還支援“-s reload”指令,可以在不重新開機服務的情況下可使配置生效。

一、Nginx配置檔案簡介

如果編譯安裝Nginx時使用預設路徑,那麼Nginx運作目錄是/usr/local/nginx,其配置檔案存放目錄是/usr/local/nginx/conf/nginx.conf。其内容預設如下:

#user  nobody;  

worker_processes  1;  

#error_log  logs/error.log;  

#error_log  logs/error.log  notice;  

#error_log  logs/error.log  info;  

#pid        logs/nginx.pid;  

events {  

    worker_connections  1024;  

}  

http {  

    include       mime.types;  

    default_type  application/octet-stream;  

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '  

    #                  '$status $body_bytes_sent "$http_referer" '  

    #                  '"$http_user_agent" "$http_x_forwarded_for"';  

    #access_log  logs/access.log  main;  

    sendfile        on;  

    #tcp_nopush     on;  

    #keepalive_timeout  0;  

    keepalive_timeout  65;  

    #gzip  on;  

    server {  

        listen       80;  

        server_name  localhost;  

        #charset koi8-r;  

        #access_log  logs/host.access.log  main;  

        location / {  

            root   html;  

            index  index.html index.htm;  

        }  

        #error_page  404              /404.html;  

        # redirect server error pages to the static page /50x.html  

        #  

        error_page   500 502 503 504  /50x.html;  

        location = /50x.html {  

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80  

        #location ~ \.php$ {  

        #    proxy_pass   http://127.0.0.1;  

        #}  

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000  

        #    root           html;  

        #    fastcgi_pass   127.0.0.1:9000;  

        #    fastcgi_index  index.php;  

        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;  

        #    include        fastcgi_params;  

        # deny access to .htaccess files, if Apache's document root  

        # concurs with nginx's one  

        #location ~ /\.ht {  

        #    deny  all;  

    }  

    # another virtual host using mix of IP-, name-, and port-based configuration  

    #  

    #server {  

    #    listen       8000;  

    #    listen       somename:8080;  

    #    server_name  somename  alias  another.alias;  

    #    location / {  

    #        root   html;  

    #        index  index.html index.htm;  

    #    }  

    #}  

    # HTTPS server  

    #    listen       443;  

    #    server_name  localhost;  

    #    ssl                  on;  

    #    ssl_certificate      cert.pem;  

    #    ssl_certificate_key  cert.key;  

    #    ssl_session_timeout  5m;  

    #    ssl_protocols  SSLv2 SSLv3 TLSv1;  

    #    ssl_ciphers  HIGH:!aNULL:!MD5;  

    #    ssl_prefer_server_ciphers   on;  

塊配置項

配置檔案中有很多塊配置項。塊配置項是由一個塊配置項名和一對大括号組成。例如上面代碼段中的http、server、event等等。也可以在塊配置項名之後後大括号之前加上參數。

塊配置項可以嵌套。内層塊直接繼承外層塊。例如上例中server塊裡的任意配置都是基于http塊裡的已有配置的。當内外層中的配置發生沖突時,究竟是以内層塊還是外層塊的配置為準取決于解析這個配置項的子產品。

配置項的文法

最基本的配置項文法格式:

配置項名 配置項值1 配置項值2 配置項值3 ... ;

行首是配置項名,這些配置項名必須是Nginx的某一個子產品想要處理的,否則Nginx會認為配置檔案出現了非法的配置項名。配置項名輸入結束後以空格作為分隔符。

其次是配置項值,可以是數字或字元串。可以由一個或多個配置項值。中間以空格分隔。

最後,行尾是分号。

以“#”開始的是注釋行。

二、怎樣使用http配置

處理http配置項可以分為以下四個步驟:

(1)建立資料結構用于存儲配置項對應的參數。

(2)設定配置項在nginx.conf中出現時的限制條件與回調方法。

(3)實作第二步中的回調方法,或者使用Nginx架構預設的14個回調方法。

(4)合并不同級别的配置塊中出現的同名配置項。

在這裡不得不提到的是兩個非常重要的資料結構:ngx_http_module_t以及ngx_command_t,是HTTP子產品時不可或缺的部分,它們把這四個步驟與Nginx有機地結合起來。

在本例中我們通過在配置檔案中添加如下項來自己編寫子產品進行解析(添加到預設server塊内):

#測試配置項2  

location /test2 {  

   test_str "hello my dear HUST!";  

   test_flag on;  

   test_num 10;  

   test_size 1000;  

   mytest;  

配置設定用于儲存配置參數的資料結構

這個資料結構依據需要儲存的參數自定義即可。

一般情況下這個結構是(包含了各種類型的配置項,但是在本例中隻實作了部分類型的配置項的解析):

//存儲配置項參數的結構體  

typedef struct{  

        ngx_str_t arg_str;//儲存一個字元串類型的參數  

        ngx_int_t arg_num;  

        ngx_flag_t arg_flag;  

        size_t arg_size;  

        ngx_array_t* arg_str_array;  

        ngx_array_t* arg_keyval;  

        off_t arg_off;  

        ngx_msec_t arg_msec;  

        time_t arg_sec;  

        ngx_bufs_t arg_bufs;  

        ngx_uint_t arg_enum_seq;  

        ngx_uint_t arg_bitmask;  

        ngx_uint_t arg_access;  

        ngx_path_t* arg_path;  

}ngx_http_mytest2_loc_conf_t;  

需要注意的是,這個結構會Nginx的記憶體中儲存許多份。http架構在解析nginx.conf檔案時,隻要遇到http{}、server{}、或者location{}配置塊就會立刻配置設定一個新的結構體。

Nginx如何管理我們自定義的存儲配置的結構體呢?

是通過ngx_http_module_t中的8個回調方法(ngx_http_config.h):

24 typedef struct {  

25     ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);  

26     ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);  

27   

28     void       *(*create_main_conf)(ngx_conf_t *cf);  

29     char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);  

30   

31     void       *(*create_srv_conf)(ngx_conf_t *cf);  

32     char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);  

33   

34     void       *(*create_loc_conf)(ngx_conf_t *cf);  

35     char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);  

36 } ngx_http_module_t;  

其中以create開頭的三個回調方法負責把我們配置設定的用于儲存配置項的結構體傳遞給http架構。為什麼會定義三個回調方法呢?

http架構定義了三個級别的配置main、srv、loc,分别表示直接出現在http{}、server{}、location{}、塊内的配置。當nginx.conf中出現http{}時,http架構會接管配置檔案中http{}塊内的配置項解析。當遇到http{}配置塊時,http架構會調用所有的http子產品可能實作的create_main_conf、create_srv_conf、create_loc_conf方法生成存儲main級别的配置參數的結構體;在遇到server{}配置塊時,會再次調用所有的http子產品可能實作的create_srv_conf、create_loc_conf方法生成存儲srv級别的配置參數的結構體;在遇到location{}配置塊時,會再次調用所有的http子產品可能實作的create_loc_conf方法生成存儲loc級别的配置參數的結構體。實作三個回調方法的意義是不同的。在一個子產品中,http塊内隻會調用一次create_main_conf,但是create_loc_conf可能會被調用很多次,也就是有許多由create_loc_conf生成的結構體。

普通http請求往往隻實作create_loc_conf回調方法,因為它們隻關注比對某種URL的請求。

設定配置項的解析方式

我們在ngx_command_t結構體中設定配置項的解析方式:

78 struct ngx_command_s {  

79     ngx_str_t             name;//配置項名稱  

80     ngx_uint_t            type;//決定這個配置項可以在哪些塊中出現以及可以攜帶的參數類型和個數  

81     char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);//回調方法,可以自己實作也可以使用預設的14個方法  

82     ngx_uint_t            conf;//配置項所處記憶體的相對偏移量  

83     ngx_uint_t            offset;//目前配置項在整個存儲配置項的結構體中的偏移位置  

84     void                 *post;//配置項的回調方法  

85 };  

在本例中,前四個配置項都用預設的方法進行解析,而最後一個配置項mytest用自定義的方法,并在這個方法中将前面各個配置項的參數組合成一個字元串傳回給客戶。

我們需要通過定義ngx_command_t數組來設定配置項的解析方式:

//設定配置項的解析方式  

static ngx_command_t ngx_http_mytest2_commands[] = {  

        {  

                //test_str配置項  

                ngx_string("test_str"),  

        NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,  

        ngx_conf_set_str_slot,//預設的配置項解析方法  

                NGX_HTTP_LOC_CONF_OFFSET,  

                offsetof(ngx_http_mytest2_loc_conf_t,arg_str),  

                NULL  

        },  

                //test_flag配置項  

                ngx_string("test_flag"),  

                ngx_conf_set_flag_slot,//預設的配置項解析方法  

        NGX_HTTP_LOC_CONF_OFFSET,  

                offsetof(ngx_http_mytest2_loc_conf_t,arg_flag),  

                //test_num配置項  

                ngx_string("test_num"),  

                ngx_conf_set_num_slot,//預設的配置項解析方法  

                offsetof(ngx_http_mytest2_loc_conf_t,arg_num),  

                //test_size配置項  

                ngx_string("test_size"),  

                ngx_conf_set_size_slot,//預設的配置項解析方法  

                offsetof(ngx_http_mytest2_loc_conf_t,arg_size),  

                //mytest配置項  

                ngx_string("mytest"),  

        NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  

        ngx_http_mytest2,  

                0,  

    ngx_null_command  

};  

其中自定義的配置項解析方法ngx_http_mytest2:

//子產品的回調方法  

static char *   

ngx_http_mytest2(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);  

    clcf->handler = ngx_http_mytest2_handler;//每當遇到配置項mytest的時候會回調這個方法  

    return NGX_CONF_OK;  

真正完成處理工作的handler是 ngx_http_mytest2_handler:

//子產品真正完成處理工作的handler  

static ngx_int_t ngx_http_mytest2_handler(ngx_http_request_t *r)  

        ngx_http_mytest2_loc_conf_t *elcf;//存儲配置項參數的結構體  

        elcf = ngx_http_get_module_loc_conf(r,ngx_http_mytest2_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 (rc != NGX_OK) {  

        return rc;  

    ngx_str_t type = ngx_string("text/plain");  

        ngx_str_t str_format = ngx_string("test_str=%V,test_flag=%i,test_num=%i,test_size=%z");  

        ngx_str_t test_str = elcf->arg_str;  

        ngx_flag_t test_flag = elcf->arg_flag;  

        ngx_int_t test_num = elcf->arg_num;  

        size_t test_size = elcf->arg_size;  

        int data_len = str_format.len + test_str.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) {  

    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 *)str_format.data,&test_str,test_flag,test_num,test_size);  

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

三、新添加子產品的編譯

普通編譯方式是:

./configure --prefix=/usr/local/nginx --add-module=XX(新子產品的config檔案以及源碼所存放的目錄)  

make  

sudo make install  

但是這樣的一個缺點是:每次都要編譯所有的nginx源碼,速度慢。如果自己編寫的新子產品中的源代碼中有錯誤,調試起來很不友善。有一個方法是自己編寫一個makefile檔案,先單獨編譯新子產品的代碼,修正所有錯誤之後再将其編譯進Nginx。

這是我編寫的MakeFile檔案:

#編譯新子產品的makefile檔案  

ngx_http_mytest_module.o: ngx_http_mytest_module.c  

    gcc -c -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g   -I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/core -I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/event -I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/event/modules -I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/os/unix -I /home/xiajun/TEST/Nginx/nginx-1.4.1/objs -I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/http -I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/http/modules -I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/mail -o ngx_http_mytest_module.o /home/xiajun/TEST/Nginx/nginx-1.4.1/mytest/ngx_http_mytest_module.c  

四、完整代碼及結果示範

config檔案:

ngx_addon_name=ngx_http_mytest2  

HTTP_MODULES="$HTTP_MODULES ngx_http_mytest2_module"  

NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mytest2_module.c"

ngx_http_mytest2_module.c:

//Nginx自定義子產品實作代碼  

//E-Mail:[email protected](江南煙雨)  

#include <ngx_config.h>  

#include <ngx_core.h>  

#include <ngx_http.h>  

static ngx_int_t   

ngx_http_mytest2_handler(ngx_http_request_t *r);  

ngx_http_mytest2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);  

static void*  

ngx_http_mytest2_create_loc_conf(ngx_conf_t *cf);  

static char*  

ngx_http_mytest2_merge_loc_conf(ngx_conf_t *cf,void *parent,void *child);  

//子產品上下文定義  

static ngx_http_module_t ngx_http_mytest2_module_ctx = {  

    NULL,  

    ngx_http_mytest2_create_loc_conf,//建立資料結構存儲loc級别的配置項的回調方法  

    ngx_http_mytest2_merge_loc_conf//合并loc級别的配置項  

//子產品定義  

ngx_module_t ngx_http_mytest2_module = {  

    NGX_MODULE_V1,  

    &ngx_http_mytest2_module_ctx,  

    ngx_http_mytest2_commands,  

    NGX_HTTP_MODULE,  

        NULL,  

    NGX_MODULE_V1_PADDING  

    clcf->handler = ngx_http_mytest2_handler;  

        //ngx_conf_set_str_slot(cf,cmd,conf);//預設的配置項處理方法  

ngx_http_mytest2_create_loc_conf(ngx_conf_t *cf){  

        ngx_http_mytest2_loc_conf_t *conf;  

        conf = ngx_pcalloc(cf->pool,sizeof(ngx_http_mytest2_loc_conf_t));  

        if(NULL == conf){  

                return NGX_CONF_ERROR;  

        conf->arg_str.len = 0;  

        conf->arg_str.data = NULL;  

        //注意一下設定必不可少,否則會出錯  

        conf->arg_flag = NGX_CONF_UNSET;  

        conf->arg_num = NGX_CONF_UNSET;  

        conf->arg_str_array = NGX_CONF_UNSET_PTR;  

        conf->arg_keyval = NULL;  

        conf->arg_off = NGX_CONF_UNSET;  

        conf->arg_msec = NGX_CONF_UNSET_MSEC;  

        conf->arg_sec = NGX_CONF_UNSET;  

        conf->arg_size = NGX_CONF_UNSET_SIZE;  

        return conf;  

ngx_http_mytest2_merge_loc_conf(ngx_conf_t *cf,void *parent,void *child){  

        ngx_http_mytest2_loc_conf_t *prev = parent;  

        ngx_http_mytest2_loc_conf_t *conf = child;  

        ngx_conf_merge_str_value(conf->arg_str,prev->arg_str,"");  

        return NGX_CONF_OK;  

結果示範:

Nginx學習之二-配置項解析及程式設計實作

五、參考資料:

<a href="http://tengine.taobao.org/book/chapter_03.html">淘寶tengine</a>

from:http://blog.csdn.net/xiajun07061225/article/details/9147265

繼續閱讀