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

li27z發表於2016-09-07

前言

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

相關文章