摘要:本文將從源碼從此深入分析配置文件的解析,配置存儲,與配置查找。在學習配置文件的解析過程之前,需要先了解一下模塊與指令的一些基本知識。
運營研發團隊 李樂
配置文件是nginx的基礎,對于學習nginx源碼甚至開發nginx模塊的同學來說更是必須深究。本文將從源碼從此深入分析nginx配置文件的解析,配置存儲,與配置查找。
看本文之前讀者可以先思考兩個問題:
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等)。
nginx規定指令塊可以嵌套(如http塊中可以嵌套server指令,server塊中可以嵌套location指令),指令可以同時出現在不同的指令塊(如root指令可以同時出現在http、server和location指令塊)。
配置文件這種層次的復雜性,導致配置文件的解析與存儲等的復雜性。
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類模塊暫不考慮)。見下表
從上表列出的三種類型的模塊上下文結構體可以看出:
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指令塊;
用于校驗參數數目。常用指令類型如下:
set:處理函數,當讀取到該配置指令時,會執行此函數;
conf和offset其實都表示的是偏移量,但是用處不同,解析指令時會詳述,這里暫時跳過。
post可以指向多種結構,不同指令可能不同,大多都為NUll,解析到具體指令時會詳述,這里同樣跳過。
下面這張圖展示了指令的基本分類(通過顏色區分,各種顏色的文字描述指令類型以及該指令只能被哪種類型的模塊解析):
1.3 配置存儲格式方案設計http配置相對復雜,h指令塊嵌套,模塊眾多,導致http配置解析與存儲的復雜性。因此本小節重點講述http相關配置存儲的方案設計。
前面提到每個模塊負責解析和存儲自己關心的配置指令,即每個模塊都應該有個可以存儲配置的結構體,該結構體通過模塊上下文結構體的函數create_conf,create_main_conf,create_srv_conf或者create_loc_conf創建。
比如說如下表:
問題來了,每個模塊創建自己的配置結構體,存儲是完全分散的,如何能快速查找到這些配置結構體呢?
最容易想到的就是聲明一個void*的數組,數組元素數目就是模塊數目,以模塊的index字段作為數組的索引,數組的每個元素都指向對應模塊的配置結構體。
但是不要忘記,nginx配置文件是有層次結構的,如http指令塊中可以聲明多個單條指令和多個server指令塊,server指令塊中可以聲明多個單條指令和多個location指令塊,location配置又可以聲明多個單條指令。
我們可以這樣來設計:
1)配置文件可以包含多條指令,指令塊同樣可以包含多條指令,為此我們可以定義指令作用域或者稱為指令上下文;
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配置存儲結構示意圖:
這個結構似乎是可以的,但是我們忘記了一件事:一些指令可以同時出現在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指令塊。
為此我們修改上面的結構如下:
上面我們分析了http指令塊內部的所有指令可能的存儲格式,events指令塊內部存儲格式相比較簡單很多,讀者可以試著畫一畫。
那么這是否是nginx采用的存儲格式呢?可以說和上圖非常類似,nginx設計的配置存儲格式見下圖,這里暫時留兩個疑問:
1)如何實現http_ctx嵌套srv_ctx,srv_ctx嵌套loc_ctx;
2)當某條指令同時出現在http指令塊、server指令塊和location指令塊時,以哪個配置為準。
總結本文作為nginx配置文件解析的第一小篇,簡要介紹了nginx模塊和指令的基本概念,同時針對http相關配置的存儲格式進行了初步設計與講解,為下文《nginx配置文件解析(二)》講解。配置文件解析源碼分析打下基礎。
希望交流,一起學習Nginx PHP Redis 等源碼的朋友請入微信群:
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/40172.html
摘要:每個模塊由以下幾部分構成結構體代表模塊本身,其指針被放入數組中。結構體用來表示模塊的配置內容,其中部分成員可以通過配置文件進行配置。調用該中的函數,該函數最終初始化模塊對應的結構體,完成配置。因此,分析源碼中的配置指令,就是分析結構體。 本篇的上篇 Nginx 源碼分析:從模塊到配置(上),建議閱讀本篇前先閱讀上篇。 關于模塊 Nginx的架構高度模塊化。每個模塊各司其職,組合在一...
摘要:四監聽套接字的使用假設此處我們使用作為事件處理模塊在增加事件時用戶可以使用中的字段當事件發生時該字段也會帶回。在創建監聽套接字時將結構分為級監聽套接字地址各級都是一對多的關系。 施洪寶 一. 基礎 nginx源碼采用1.15.5 后續部分僅討論http中的listen配置解析以及優化流程 1.1 概述 假設nginx http模塊的配置如下 http{ server { ...
摘要:表示的是兩個,當其中任意一個計算完并發編程之是線程安全并且高效的,在并發編程中經??梢娝氖褂?,在開始分析它的高并發實現機制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個比較典型的互聯網高并發場景。 干貨:深度剖析分布式搜索引擎設計 分布式,高可用,和機器學習一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個名詞,今天我們首先來說說分布式。 探究...
閱讀 2016·2021-11-12 10:36
閱讀 1865·2021-11-09 09:49
閱讀 2591·2021-11-04 16:12
閱讀 1144·2021-10-09 09:57
閱讀 3235·2019-08-29 17:24
閱讀 1909·2019-08-29 15:12
閱讀 1272·2019-08-29 14:07
閱讀 1285·2019-08-29 12:53