《深刻理解Nginx》閱讀與實踐(二):配置項的使用

  前文連接:《深刻理解Nginx》閱讀與實踐(一):Nginx安裝配置與HelloWorldhtml

 

  HelloWorld的完成意味着已經踏入了nginx的大門,雖然很振奮人心,但在編寫中仍有不少疑惑的存在:nginx.conf的配置項中各個參數是如何讀入程序中的?ngx_command_t如何完成配置項的讀入工做?名稱相同的配置項的衝突如何解決?HelloWorld中的ngx_http_module_t何以稱爲模塊的上下文?同時我在讀第4章"配置項的使用"時又有成見:不就是各類瑣碎的參數設置嘛,有什麼好讀的(這個成見來自於UNP中某一章節套接字選項)不過通過仔細閱讀並實踐這部份內容以後,我發現徹底八竿子打不着要點。其實這裏的配置項的使用,是指將咱們在nginx.conf中設置的配置項的參數傳遞到nginx程序的過程。同時,通過這一章,你將學到如何將一個「根據用戶請求的URI返回HelloWorld的模塊」變成一個「能夠完成解析各類nginx配置項的模塊」。nginx

 

1、存放配置項的值的數據結構數組

  先來看看文中介紹的保存配置參數的數據結構ngx_http_mytest_conf_t:數據結構

typedef struct {
    ngx_str_t       my_str;
    ngx_int_t       my_num;
    ngx_flag_t      my_flag;
    size_t          my_size;
    ngx_array_t*    my_str_array;
    ngx_array_t*    my_keyval;
    off_t            my_off;
    ngx_msec_t      my_msec;
    time_t          my_sec;
    ngx_bufs_t      my_bufs;
    ngx_uint_t      my_enum_seq;
    ngx_uint_t      my_bitmask;
    ngx_uint_t      my_access;
    ngx_path_t*     my_path;
} ngx_http_mytest_conf_t;
ngx_http_mytest_conf_t

  與原書提到的問題(爲何不用全局變量來存儲)不一樣,個人問題是,既然想要統一用一個結構體存放各類各樣的配置項如整數、標記(非0即1)、字符串、時間等等,爲何不用一個聯合從而節省空間?帶着疑問繼續往下讀纔會發現:這裏的數據結構是爲了便於後面說明預設配置項解析方法而定義的,它能處理的配置項是下面這種形式:ide

name [ngx_str_t] [ngx_int_t] ... [ngx_path_t*] [[ngx_str_t][ngx_int_t]]

這種配置項的名稱後面能夠跟着14種參數以及在後文定義的myconfig的2個參數,這16個參數能夠所有出現,也能夠只出現其中之一。也即,它的功能並非僅僅處理配置項名+單一參數的配置項,而是全部可能形式的配置項。這樣複雜在所不免,若是實際使用的配置項是咱們所能控制的有限幾種,沒有必要設計這麼複雜,正如HelloWorld中mytest配置項後根本就沒有值,只是設立了個遇到該配置項就啓動mytest模塊的函數而已,那時甚至沒有必要設計這麼一個保存配置項的值的結構。函數

 

2、對配置項的值的操做:讀取與合併post

  回顧HelloWorld中的表明模塊上下文的ngx_http_module_t結構,以前雖然提到它是模塊的上下文,使得模塊具備本身的特性,但當時把它的8個回調方法都設置成了NULL,也看不出什麼名堂。這時《深刻理解Nginx》對它作了個回顧並說明,我是這樣理解的:所謂「上文」,就是作preconfiguration的工做、處理nginx.conf的配置項(將全部配置項讀入、創建數據結構保存、各級同名配置項合併);「下文」就是在處理完配置項後調用函數postconfiguration進行一些後續工做。ui

static ngx_http_module_t  ngx_http_mytest_module_ctx =
{
    NULL,                              /* preconfiguration */
    ngx_http_mytest_post_conf,      /* postconfiguration */

    NULL,                              /* create main configuration */
    NULL,                              /* init main configuration */

    NULL,                              /* create server configuration */
    NULL,                              /* merge server configuration */

    ngx_http_mytest_create_loc_conf, /* create location configuration */
    ngx_http_mytest_merge_loc_conf   /* merge location configuration */
};

  爲了便於說明,這裏只實現了create_loc_conf和merge_loc_conf,前者在nginx.conf中的每個http{...}、server{...}、location{...}都會調用來爲ngx_http_mytest_conf_t賦初值;後者用來合併create_loc_conf生成的全部ngx_http_mytest_conf_t的配置項。url

  除此之外,爲了打印出讀取的配置項的值,須要實現ngx_http_mytest_post_conf,在處理完配置項後調用。這樣,模塊的上下文便寫好了。spa

關於create_loc_conf、create_srv_conf、create_main_conf的調用時機:

  create_loc_conf是遇到每個http{...}、server{...}、location{...}時;

  create_srv_conf是遇到每個server{...}、location{...}時;

  create_main_conf只在遇到http{...}時。

  這個細節能夠參考原書。

關於merge_srv_conf的調用做用:合併main級別和server級別的配置項,請參考10.2.4。

 

  這時就能夠經過設定ngx_command_s來設定配置項的解析方式了,簡單歸納就是配置項的名稱、它所能出現的位置、對值處理的回調方法也即解析方法、值存放於以前定義的數據結構的偏移量。這一系列解析方式構成了一個數組,用來解析全部可能出現的配置項的形式。這裏原書說的比較詳細,就再也不重複。對應14種配置項的值的類型,書上講了14種解析方式和針對自定義配置項的處理方法(對應的回調函數是ngx_conf_set_myconfig)。

  這裏不得不提一下ngx_conf_t類型。原書中屢次出如今函數原型形參中但並未說起,我查了一些資料,它的定義以下:

struct ngx_conf_s {
    char                 *name;
    ngx_array_t          *args;

    ngx_cycle_t          *cycle;
    ngx_pool_t           *pool;
    ngx_pool_t           *temp_pool;
    ngx_conf_file_t      *conf_file;
    ngx_log_t            *log;

    void                 *ctx;
    ngx_uint_t            module_type;
    ngx_uint_t            cmd_type;

    ngx_conf_handler_pt   handler;
    char                 *handler_conf;
};

http://kenby.iteye.com/blog/1169675中提到,「解析配置文件的時候, 用結構體ngx_conf_s來暫時存放指令的參數」。同時查閱nginx源碼,排除掉做爲函數形參時的出現,出如今下面幾處,做用和這個描述很相似:

  src\core\Ngx_cycle.c

  src\event\Ngx_event.c

  src\http\Ngx_http.c 

  src\http\Ngx_http_script.h

  src\mail\Ngx_mail.c

  那麼就暫時不去管它,看成一箇中間數據的結構體就好了。

 

  原書以test_str配置項爲例,編寫了用於合併test_str類型的ngx_http_mytest_loc_conf()函數,並能夠看出當父子配置塊有相同配置項的處理方法:以父配置塊爲準或子配置塊爲準都可,能夠自由選擇,或者進行運算生成。原書後文是更詳細的配置塊合併時的數據結構存儲模型和合並邏輯的歸納,值得仔細揣摩。

 

3、error日誌和請求上下文

  這一部分沒有什麼好寫的,原書已經很詳細了。不過error日誌的輸出會在後面用到。

 

4、把HelloWorld變成配置項輸出

  按着第4章的線索,對HelloWorld作出相應修改就能完成這個轉變了。其實總體邏輯組成和第3章圖示差很少,只是具體實現方法不大同樣:

  1. 定義結構體ngx_http_mytest_conf_t;
  2. 原先的ngx_module_t類型ngx_http_mytest_module以及config文件保持不變;
  3. 將原先的全部成員爲空的模塊上下文ngx_http_module_t進行修改,添加回調函數create_loc_conf()、merge_loc_conf()、ngx_http_mytest_post_conf()並實現(ngx_http_mytest_post_conf()能夠放在最後來進行);
  4. 爲全部值類型編寫解析方式ngx_command_t  ngx_http_mytest_commands[]替代原先的,在此以前須要實現解析自定義類型的ngx_conf_set_myconfig()方法;
  5. 原先用於處理HTTP報文並返回請求的ngx_http_mytest_handler已無用,刪去;
  6. 編寫ngx_http_mytest_post_conf()取的不是這個模塊的信息而是已傳遞給ngx_http_module和ngx_http_core_module的數據所以須要在源碼中extern進兩者。ngx_http_mytest_post_conf()的具體實現有些超出本章內容,不作詳解。
  7. 將nginx.conf修改,在各個配置塊中增長test_str,從新編譯運行源碼,就能夠在屏幕上看到相應的輸出了,以下圖所示:

 

  完整的源碼能夠在《深刻理解Nginx》做者的頁面上進行下載:http://nginx.weebly.com/31034203632830430721.html

相關文章
相關標籤/搜索