【Nginx源碼分析】Nginx配置文件解析(一)

運營研發團隊 李樂nginx

配置文件是nginx的基礎,對於學習nginx源碼甚至開發nginx模塊的同窗來講更是必須深究。本文將從源碼今後深刻分析nginx配置文件的解析,配置存儲,與配置查找。segmentfault

看本文以前讀者能夠先思考兩個問題:數組

  • 1.nginx源碼中隨處能夠看到相似於這樣的代碼。
//獲取限流相關配置
lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
//獲取fastcgi相關配置
flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);

爲何能夠這樣獲取到限流和fastcgi相關的配置呢?微信

  • 2.server配置塊中能夠有多個location配置,那麼location配置的匹配優先級是怎樣的?好比說配置了三個location:「location ^~ /a { }」、「location /a/b { }」和「location ~ /a/* { }」,請求url爲/a/b,最終能匹配到哪一個location配置塊呢?

相信學習本文以後,這兩個問題將不在話下。函數

在學習nginx配置文件的解析過程以前,須要先了解一下nginx模塊與指令的一些基本知識。源碼分析

nginx的配置指令能夠分爲兩大類:指令塊(如events、http、server和location)與單條指令(如worker_processes、root、rewrite等)。post

nginx規定指令塊能夠嵌套(如http塊中能夠嵌套server指令,server塊中能夠嵌套location指令),指令能夠同時出如今不一樣的指令塊(如root指令能夠同時出如今http、server和location指令塊)。學習

配置文件這種層次的複雜性,致使配置文件的解析與存儲等的複雜性。ui

1.1 nginx模塊

結構體ngx_module_t用於定義一個nginx模塊,這裏須要重點關注如下幾個字段。編碼

struct ngx_module_s {
    ngx_uint_t            ctx_index; //用於給同類型的模塊編號
    ngx_uint_t            index;  //用於給全部模塊編號

    void                 *ctx;  //模塊上下文;很重要;不一樣類型的模塊一般指向不一樣類型的結構體,結構體一般包含若干函數指針
    ngx_command_t        *commands; //指令數組
    ngx_uint_t            type;  //模塊類型編碼

type字段表示模塊類型編碼。ctx指向模塊上下文結構體,且不一樣類型的模塊一般指向不一樣類型的結構體,該結構體中一般會包含若干函數指針。

nginx經常使用模塊能夠分爲這麼幾類:核心模塊,事件模塊語http模塊(conf類模塊與mail類模塊暫不考慮)。見下表

clipboard.png

從上表列出的三種類型的模塊上下文結構體能夠看出:

  • 1)核心模塊上下文結構只有三個字段:name表示核心模塊名稱;create_conf用於建立模塊配置結構體;init_conf用於初始化模塊配置結構體;
  • 2)事件模塊上下文結構前三個字段與核心模塊相同,可是多了一個類型爲ngx_event_actions_t結構的字段;該結構一樣包含若干函數指針,表示該事件模塊對外提供的若干API,好比添加事件還與刪除事件等,這裏不作詳述;
  • 3)咱們都知道http相關配置能夠分爲三類,http指令塊、server指令塊和location指令塊,對應的配置結構體稱爲main_conf、srv_conf和loc_conf;相應的create_conf和init_conf方法用於建立和初始化相關配置結構體。

而http模塊上下文結構的preconfiguration和postconfiguration用於初始化http處理流程相關操做。

index字段用於給全部模塊編號,好比:

ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
    ngx_modules[i]->index = ngx_max_module++;
}
ctx_index用於給同類型的模塊編號,好比:

ngx_http_max_module = 0;
for (m = 0; ngx_modules[m]; m++) {
    if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
        continue;
    }

    ngx_modules[m]->ctx_index = ngx_http_max_module++;
}

1.2 nginx配置指令

nginx的各個模塊組合造成了其強大的處理能力,而每一個模塊只實現一個特定的功能。好比限流功能由模塊ngx_http_limit_conn_module或者模塊實現ngx_http_limit_req_module;fastcgi轉發功能由模塊ngx_http_fastcgi_module

實現;proxy轉發功能由ngx_http_proxy_module(固然轉發功能的實現還必須有模塊ngx_http_upstream_module)。

當咱們配置了指令proxy_pass或者fastcgi_pass時,該指令應該由哪一個模塊來解析呢?顯然應該由實現此功能的模塊來解析。即nginx配置文件的解析是分散到各個模塊的。

每一個模塊都有一個commands數組,存儲該模塊能夠解析的全部配置指令。指令結構體由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;
};
  • name:配置指令名稱,如「proxy_pass」;
  • type:指令類型,能夠將指令類型分爲兩類,1)說明指令能夠出現的位置,好比配置文件(只能在配置文件最外層,不能出如今任何指令塊內部),http指令塊,或者events指令塊,或者server指令塊,或者location指令塊;

用於校驗參數數目。經常使用指令類型以下:

clipboard.png

  • set:處理函數,當讀取到該配置指令時,會執行此函數;
  • conf和offset其實都表示的是偏移量,可是用處不一樣,解析指令時會詳述,這裏暫時跳過。
  • post能夠指向多種結構,不一樣指令可能不一樣,大多都爲NUll,解析到具體指令時會詳述,這裏一樣跳過。

下面這張圖展現了指令的基本分類(經過顏色區分,各類顏色的文字描述指令類型以及該指令只能被哪一種類型的模塊解析):

clipboard.png

1.3 配置存儲格式方案設計

http配置相對複雜,h指令塊嵌套,模塊衆多,致使http配置解析與存儲的複雜性。所以本小節重點講述http相關配置存儲的方案設計。

前面提到每一個模塊負責解析和存儲本身關心的配置指令,即每一個模塊都應該有個能夠存儲配置的結構體,該結構體經過模塊上下文結構體的函數create_conf,create_main_conf,create_srv_conf或者create_loc_conf建立。

好比說以下表:

clipboard.png

問題來了,每一個模塊建立本身的配置結構體,存儲是徹底分散的,如何能快速查找到這些配置結構體呢?

最容易想到的就是聲明一個void*的數組,數組元素數目就是模塊數目,以模塊的index字段做爲數組的索引,數組的每一個元素都指向對應模塊的配置結構體。

可是不要忘記,nginx配置文件是有層次結構的,如http指令塊中能夠聲明多個單條指令和多個server指令塊,server指令塊中能夠聲明多個單條指令和多個location指令塊,location配置又能夠聲明多個單條指令。

咱們能夠這樣來設計:

  • 1)配置文件能夠包含多條指令,指令塊一樣能夠包含多條指令,爲此咱們能夠定義指令做用域或者稱爲指令上下文;

clipboard.png

  • 2)指令塊的嵌套等價爲上下文的嵌套,而上下文表現爲某種類型的結構體,所以可經過結構體的互相引用實現指令塊的嵌套;
  • 3)指令或者指令塊只能被特定類型的模塊解析。好比,配置文件上下文包含的全部指令只能被核心模塊(NGX_CORE_MODULE)解析;events指令塊包含的全部指令只能被事件模塊(NGX_EVENT_MODULE)解析;

http指令塊內包含的全部指令或者指令塊只能被http模塊(NGX_HTTP_MODULE)解析。

  • 4)http模塊能夠解析http指令塊,server指令塊和location指令塊的指令;所以,http模塊的指令結構分爲3種:main_conf、srv_conf和loc_conf,其經過函數create_main_conf,create_srv_conf和create_loc_conf建立。

參考這四點設計,咱們能夠簡單畫出http配置存儲結構示意圖:

clipboard.png

這個結構彷佛是能夠的,可是咱們忘記了一件事:一些指令能夠同時出如今http指令塊、server指令塊和location指令塊。

即http塊中的指令類型能夠是NGX_HTTP_MAIN_CONF,也能夠是NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF,還能夠是NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONFNGX_HTTP_LOC_CONF;

而server塊中的指令類型能夠是NGX_HTTP_SRV_CONF,也能夠是NGX_HTTP_SRV_CONFNGX_HTTP_LOC_CONF。(位或運算表示同時屬於多種類型)

好比說指令root的類型位NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF,此時該配置應該存儲在loc_conf配置結構,可是其可能會配置在http指令塊中、server指令塊或者location指令塊。

爲此咱們修改上面的結構以下:

clipboard.png

上面咱們分析了http指令塊內部的全部指令可能的存儲格式,events指令塊內部存儲格式相比較簡單不少,讀者能夠試着畫一畫。

那麼這是不是nginx採用的存儲格式呢?能夠說和上圖很是相似,nginx設計的配置存儲格式見下圖,這裏暫時留兩個疑問:

  • 1)如何實現http_ctx嵌套srv_ctx,srv_ctx嵌套loc_ctx;
  • 2)當某條指令同時出如今http指令塊、server指令塊和location指令塊時,以哪一個配置爲準。

clipboard.png

總結

本文做爲nginx配置文件解析的第一小篇,簡要介紹了nginx模塊和指令的基本概念,同時針對http相關配置的存儲格式進行了初步設計與講解,爲下文《nginx配置文件解析(二)》講解。配置文件解析源碼分析打下基礎。

但願交流,一塊兒學習Nginx PHP Redis 等源碼的朋友請入微信羣:

clipboard.png

相關文章
相關標籤/搜索