nginx學習筆記(1):配置項的解析

前言

nginx.conf默認存放在目錄/usr/local/nginx/conf下。

nginx配置文件的格式是由作者自己定義的,它沒有采用像語法分析生成器LEMON那種經典的LALR(1)來描述配置信息,而是採用一種近似key-value對的形式。這種自定義格式的配置文件的好處就是自由、靈活,而壞處就是對於nginx的每一項配置信息都必須做針對性的解析和設置,因此我們很容易看到nginx源碼裏有大量篇幅的配置信息解析與賦值代碼。

配置項格式

nginx配置文件是由多個配置項組成的,每一個配置項都有一個項目名和對應的項目值,也可能是由簡單字符串和多個配置項組合而成的配置塊的複雜結構。

我們可以將配置項歸納爲兩種:簡單配置項和複雜配置項

1.簡單配置項,例如:
worker_process 2;

2.複雜配置項,例如:
events {
    worker_connections 2000;
    use kqueue;
}

配置項相關數據結構

nginx利用ngx_command_s數據類型對所有的nginx配置項進行了統一的描述。

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

下面解釋一下結構體中的各字段的含義:

(1)字段name指定配置項的名稱。

(2)字段type將指定配置項的多種相關信息:

I.可指定該配置項的類型,例如:

NGX_CONF_FLAG:配置項攜帶的參數只能是1個,並且參數的值只能是on或者off

NGX_CONF_BLOCK:表示該配置項是複雜配置項,因此有一個由大括號組織起來的多值塊。
例如,http、server、location等配置,它們的type都必須定義爲NGX_CONF_BLOCK

NGX_CONF_ANY:不驗證配置項攜帶的參數個數

......等等

II.可指定配置項的參數個數,例如:

NGX_CONF_NOARGS:配置項不攜帶任何參數  

NGX_CONF_TAKE1:配置項必須攜帶1個參數
NGX_CONF_TAKE2:配置項必須攜帶2個參數
......

NGX_CONF_TAKE12:配置項可以攜帶1個參數或2個參數
NGX_CONF_TAKE13:配置項可以攜帶1個參數或3個參數
......等等      

III.可指定配置項在哪些{ }配置塊中出現,例如:

NGX_MAIN_CONF:配置項可以出現在全局配置中,即不屬於任何{}配置塊

NGX_EVENT_CONF: 配置項可以出現在events{}塊內 

NGX_HTTP_MAIN_CONF:配置項可以出現在http{}塊內

NGX_HTTP_SRV_CONF:配置項可以出現在server{}塊內,然而該server塊必須屬於http{}塊

......等等

(3)字段set指向配置指令處理回調函數(我們既可以自己實現一個回調方法來處理配置項,也可以使用nginx預設的14個解析配置項方法)。

預設的配置項解析回調函數,例如:

ngx_conf_set_flag_slot():如果nginx.conf文件中某個配置項的參數是on或者off(即希望配置項表達打開或者關閉某個功能的意思),
而且在Nginx模塊的代碼中使用ngx_flag_t變量來保存這個配置項的參數,就可以將set回調方法設爲ngx_conf_set_flag_slot()。
當nginx.conf文件中參數是on時,代碼中的ngx_flag_t類型變量將設爲1,參數爲off時則設爲O 

......等等  

(4)字段conf用於指示配置項所處內存的相對偏移位置,僅在type中沒有設置NGX_DIRECT_CONF和NGX_MAIN_CONF時纔會生效。
它主要由模塊NGX_HTTP_MODULE類型模塊使用,取值如下:

NGX_HTTP_MAIN_CONF_OFFSET:使用create_main_conf方法產生的結構體來存儲解析出的配置項參數

NGX_HTTP_SRV_CONF_OFFSET:使用create_srv_conf方法產生的結構體來存儲解析出的配置項參數

NGX_HTTP_LOC_CONF_OFFSET:使用create_loc_conf方法產生的結構體來存儲解析出的配置項參數

(5)offset表示當前配置項在整個存儲配置項的結構體中的偏移位置(利用offsetof宏)。

(6)如果自定義了配置項的回調方法,那麼post指針的用途完全由用戶來定義。如果不使用它,那麼設爲NULL即可。如果想將一些數據結構或者方法的指針傳過來,那麼使用post也可以。


每個模塊都把自己所需要的配置項的對應ngx_command_s結構體變量組成一個數組,並以ngx_xxx_xxx_commands的形式命名,該數組以元素ngx_null_command作爲結束哨兵。

#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL }

配置文件的基本解析流程

1.流程圖:
這裏寫圖片描述

2.先來看一下ngx_conf_parse()函數:

函數原型:
char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename);

第一個參數爲ngx_conf_t變量,第二個參數是保存配置文件路徑的字符串。
它是一個間接的遞歸函數,也就是說雖然我們在該函數體內看不到直接的對其本身的調用,但是它執行的一些函數(比如ngx_conf_handler())內又會調用ngx_conf_parse()函數,因此形成遞歸,這一般在處理一些特殊配置指令或複雜配置項,比如指令include、events、http、 server、location等的處理時。

ngx_conf_parse()函數基本將配置內容的解析過程分爲三個步驟:
判斷當前解析狀態;
讀取配置標記token;
處理配置標記token,將配置值轉換爲nginx內對應控制變量的值。

(1)判斷當前解析狀態,當前解析狀態有三種可能:
I.剛開始解析一個配置文件,即此時的參數filename指向一個配置文件路徑字符串,需要函數ngx_conf_parse()打開該文件並獲取相關的文件信息以便下面代碼讀取文件內容並進行解析,除了在上面介紹的nginx啓動時開始主配置文件解析時屬於這種情況,還有當遇到include指令時也將以這種狀態調用ngx_conf_parse()函數,因爲include指令表示一個新的配置文件要開始解析。狀態標記爲type = parse_file;。

II.開始解析一個配置塊,即此時配置文件已經打開並且也已經對文件部分進行了解析,當遇到複雜配置項比如events、http等時,這些複雜配置項的處理函數又會遞歸的調用ngx_conf_parse()函數,此時解析的內容還是來自當前的配置文件,因此無需再次打開它,狀態標記爲type =parse_block;。

III.開始解析配置項,這在對用戶通過命令行-g參數輸入的配置信息進行解析時處於這種狀態,如:
nginx -g ‘daemon on;’
nginx在調用ngx_conf_parse()函數對配置信息’daemon on;’進行解析時就是這種狀態,狀態標記爲type = parse_param;。

(2)判斷好解析狀態之後,接下來就要讀取配置內容,函數ngx_conf_read_token()就是來做這個事情的。

函數原型:
static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf);

ngx_conf_read_token()函數主要完成的工作如下:
I.讀取文件內容,每次讀取一個buf大小(4K),如果文件內容不足4K則全部讀取到buf中
II.掃描buf中的內容,每次掃描一個token就會存入cf->args中,然後返回
III.返回後ngx_conf_parse()函數會調用*cf->handler和ngx_conf_handler(cf, rc)函數處理
IV.如果是複雜配置項,會調用上次執行的狀態繼續解析配置文件.

ngx_conf_read_token()函數的返回值決定了ngx_conf_parse()函數接下來的處理,對應關係如下表:
這裏寫圖片描述

(3)對於簡單/複雜配置項的處理,一般情況下,通過函數ngx_conf_handler()來進行的(也有特殊的情況,也就是配置項提供了自定義的處理函數)。

函數原型:
static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last);

第一個參數爲ngx_conf_t變量,第二個參數記錄的是最近一次token解析函數
ngx_conf_read_token()的返回值

3.函數ngx_conf_handler()也做了三件事情:
首先,它需要找到當前解析出來的配置項所對應的 ngx_command_s結構體,如果沒找到配置項所對應的 ngx_command_s結構體,nginx就直接進行報錯並退出程序。

其次,找到當前解析出來的配置項所對應的ngx_command_s結構體之後還需進行一些有效性驗證,比如配置項的類型、位置、帶參數的個數等等。只有經過了嚴格有效性驗證的配置項才調用其對應的回調函數。

第三件事情就是調用其對應的回調函數進行處理。在處理函數內,根據實際的需要又可能再次調用函數ngx_conf_parse,如此反覆直至所有配置信息都被處理完。

以上便是nginx配置文件的基本的解析流程。


參考資料:
高羣凱.深入剖析nginx.北京:人民郵電出版社,2013

發佈了44 篇原創文章 · 獲贊 34 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章