摘要:本文將從源碼從此深入分析配置文件的解析,配置存儲,與配置查找。在學(xué)習(xí)配置文件的解析過程之前,需要先了解一下模塊與指令的一些基本知識。
運(yùn)營研發(fā)團(tuán)隊(duì) 李樂
配置文件是nginx的基礎(chǔ),對于學(xué)習(xí)nginx源碼甚至開發(fā)nginx模塊的同學(xué)來說更是必須深究。本文將從源碼從此深入分析nginx配置文件的解析,配置存儲,與配置查找。
看本文之前讀者可以先思考兩個(gè)問題:
1.nginx源碼中隨處可以看到類似于這樣的代碼。
//獲取限流相關(guān)配置 lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); //獲取fastcgi相關(guān)配置 flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
為什么可以這樣獲取到限流和fastcgi相關(guān)的配置呢?
2.server配置塊中可以有多個(gè)location配置,那么location配置的匹配優(yōu)先級是怎樣的?比如說配置了三個(gè)location:“l(fā)ocation ^~ /a { }”、“l(fā)ocation /a/b { }”和“l(fā)ocation ~ /a/* { }”,請求url為/a/b,最終能匹配到哪個(gè)location配置塊呢?
相信學(xué)習(xí)本文之后,這兩個(gè)問題將不在話下。
在學(xué)習(xí)nginx配置文件的解析過程之前,需要先了解一下nginx模塊與指令的一些基本知識。
nginx的配置指令可以分為兩大類:指令塊(如events、http、server和location)與單條指令(如worker_processes、root、rewrite等)。
nginx規(guī)定指令塊可以嵌套(如http塊中可以嵌套server指令,server塊中可以嵌套location指令),指令可以同時(shí)出現(xiàn)在不同的指令塊(如root指令可以同時(shí)出現(xiàn)在http、server和location指令塊)。
配置文件這種層次的復(fù)雜性,導(dǎo)致配置文件的解析與存儲等的復(fù)雜性。
1.1 nginx模塊結(jié)構(gòu)體ngx_module_t用于定義一個(gè)nginx模塊,這里需要重點(diǎn)關(guān)注以下幾個(gè)字段。
struct ngx_module_s { ngx_uint_t ctx_index; //用于給同類型的模塊編號 ngx_uint_t index; //用于給所有模塊編號 void *ctx; //模塊上下文;很重要;不同類型的模塊通常指向不同類型的結(jié)構(gòu)體,結(jié)構(gòu)體通常包含若干函數(shù)指針 ngx_command_t *commands; //指令數(shù)組 ngx_uint_t type; //模塊類型編碼
type字段表示模塊類型編碼。ctx指向模塊上下文結(jié)構(gòu)體,且不同類型的模塊通常指向不同類型的結(jié)構(gòu)體,該結(jié)構(gòu)體中通常會包含若干函數(shù)指針。
nginx常用模塊可以分為這么幾類:核心模塊,事件模塊語http模塊(conf類模塊與mail類模塊暫不考慮)。見下表
從上表列出的三種類型的模塊上下文結(jié)構(gòu)體可以看出:
1)核心模塊上下文結(jié)構(gòu)只有三個(gè)字段:name表示核心模塊名稱;create_conf用于創(chuàng)建模塊配置結(jié)構(gòu)體;init_conf用于初始化模塊配置結(jié)構(gòu)體;
2)事件模塊上下文結(jié)構(gòu)前三個(gè)字段與核心模塊相同,但是多了一個(gè)類型為ngx_event_actions_t結(jié)構(gòu)的字段;該結(jié)構(gòu)同樣包含若干函數(shù)指針,表示該事件模塊對外提供的若干API,比如添加事件還與刪除事件等,這里不做詳述;
3)我們都知道http相關(guān)配置可以分為三類,http指令塊、server指令塊和location指令塊,對應(yīng)的配置結(jié)構(gòu)體稱為main_conf、srv_conf和loc_conf;相應(yīng)的create_conf和init_conf方法用于創(chuàng)建和初始化相關(guān)配置結(jié)構(gòu)體。
而http模塊上下文結(jié)構(gòu)的preconfiguration和postconfiguration用于初始化http處理流程相關(guān)操作。
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的各個(gè)模塊組合形成了其強(qiáng)大的處理能力,而每個(gè)模塊只實(shí)現(xiàn)一個(gè)特定的功能。比如限流功能由模塊ngx_http_limit_conn_module或者模塊實(shí)現(xiàn)ngx_http_limit_req_module;fastcgi轉(zhuǎn)發(fā)功能由模塊ngx_http_fastcgi_module
實(shí)現(xiàn);proxy轉(zhuǎn)發(fā)功能由ngx_http_proxy_module(當(dāng)然轉(zhuǎn)發(fā)功能的實(shí)現(xiàn)還必須有模塊ngx_http_upstream_module)。
當(dāng)我們配置了指令proxy_pass或者fastcgi_pass時(shí),該指令應(yīng)該由哪個(gè)模塊來解析呢?顯然應(yīng)該由實(shí)現(xiàn)此功能的模塊來解析。即nginx配置文件的解析是分散到各個(gè)模塊的。
每個(gè)模塊都有一個(gè)commands數(shù)組,存儲該模塊可以解析的所有配置指令。指令結(jié)構(gòu)體由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)說明指令可以出現(xiàn)的位置,比如配置文件(只能在配置文件最外層,不能出現(xiàn)在任何指令塊內(nèi)部),http指令塊,或者events指令塊,或者server指令塊,或者location指令塊;
用于校驗(yàn)參數(shù)數(shù)目。常用指令類型如下:
set:處理函數(shù),當(dāng)讀取到該配置指令時(shí),會執(zhí)行此函數(shù);
conf和offset其實(shí)都表示的是偏移量,但是用處不同,解析指令時(shí)會詳述,這里暫時(shí)跳過。
post可以指向多種結(jié)構(gòu),不同指令可能不同,大多都為NUll,解析到具體指令時(shí)會詳述,這里同樣跳過。
下面這張圖展示了指令的基本分類(通過顏色區(qū)分,各種顏色的文字描述指令類型以及該指令只能被哪種類型的模塊解析):
1.3 配置存儲格式方案設(shè)計(jì)http配置相對復(fù)雜,h指令塊嵌套,模塊眾多,導(dǎo)致http配置解析與存儲的復(fù)雜性。因此本小節(jié)重點(diǎn)講述http相關(guān)配置存儲的方案設(shè)計(jì)。
前面提到每個(gè)模塊負(fù)責(zé)解析和存儲自己關(guān)心的配置指令,即每個(gè)模塊都應(yīng)該有個(gè)可以存儲配置的結(jié)構(gòu)體,該結(jié)構(gòu)體通過模塊上下文結(jié)構(gòu)體的函數(shù)create_conf,create_main_conf,create_srv_conf或者create_loc_conf創(chuàng)建。
比如說如下表:
問題來了,每個(gè)模塊創(chuàng)建自己的配置結(jié)構(gòu)體,存儲是完全分散的,如何能快速查找到這些配置結(jié)構(gòu)體呢?
最容易想到的就是聲明一個(gè)void*的數(shù)組,數(shù)組元素?cái)?shù)目就是模塊數(shù)目,以模塊的index字段作為數(shù)組的索引,數(shù)組的每個(gè)元素都指向?qū)?yīng)模塊的配置結(jié)構(gòu)體。
但是不要忘記,nginx配置文件是有層次結(jié)構(gòu)的,如http指令塊中可以聲明多個(gè)單條指令和多個(gè)server指令塊,server指令塊中可以聲明多個(gè)單條指令和多個(gè)location指令塊,location配置又可以聲明多個(gè)單條指令。
我們可以這樣來設(shè)計(jì):
1)配置文件可以包含多條指令,指令塊同樣可以包含多條指令,為此我們可以定義指令作用域或者稱為指令上下文;
2)指令塊的嵌套等價(jià)為上下文的嵌套,而上下文表現(xiàn)為某種類型的結(jié)構(gòu)體,因此可通過結(jié)構(gòu)體的互相引用實(shí)現(xiàn)指令塊的嵌套;
3)指令或者指令塊只能被特定類型的模塊解析。比如,配置文件上下文包含的所有指令只能被核心模塊(NGX_CORE_MODULE)解析;events指令塊包含的所有指令只能被事件模塊(NGX_EVENT_MODULE)解析;
http指令塊內(nèi)包含的所有指令或者指令塊只能被http模塊(NGX_HTTP_MODULE)解析。
4)http模塊可以解析http指令塊,server指令塊和location指令塊的指令;因此,http模塊的指令結(jié)構(gòu)分為3種:main_conf、srv_conf和loc_conf,其通過函數(shù)create_main_conf,create_srv_conf和create_loc_conf創(chuàng)建。
參考這四點(diǎn)設(shè)計(jì),我們可以簡單畫出http配置存儲結(jié)構(gòu)示意圖:
這個(gè)結(jié)構(gòu)似乎是可以的,但是我們忘記了一件事:一些指令可以同時(shí)出現(xiàn)在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。(位或運(yùn)算表示同時(shí)屬于多種類型)
比如說指令root的類型位NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF,此時(shí)該配置應(yīng)該存儲在loc_conf配置結(jié)構(gòu),但是其可能會配置在http指令塊中、server指令塊或者location指令塊。
為此我們修改上面的結(jié)構(gòu)如下:
上面我們分析了http指令塊內(nèi)部的所有指令可能的存儲格式,events指令塊內(nèi)部存儲格式相比較簡單很多,讀者可以試著畫一畫。
那么這是否是nginx采用的存儲格式呢?可以說和上圖非常類似,nginx設(shè)計(jì)的配置存儲格式見下圖,這里暫時(shí)留兩個(gè)疑問:
1)如何實(shí)現(xiàn)http_ctx嵌套srv_ctx,srv_ctx嵌套loc_ctx;
2)當(dāng)某條指令同時(shí)出現(xiàn)在http指令塊、server指令塊和location指令塊時(shí),以哪個(gè)配置為準(zhǔn)。
總結(jié)本文作為nginx配置文件解析的第一小篇,簡要介紹了nginx模塊和指令的基本概念,同時(shí)針對http相關(guān)配置的存儲格式進(jìn)行了初步設(shè)計(jì)與講解,為下文《nginx配置文件解析(二)》講解。配置文件解析源碼分析打下基礎(chǔ)。
希望交流,一起學(xué)習(xí)Nginx PHP Redis 等源碼的朋友請入微信群:
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/40172.html
摘要:每個(gè)模塊由以下幾部分構(gòu)成結(jié)構(gòu)體代表模塊本身,其指針被放入數(shù)組中。結(jié)構(gòu)體用來表示模塊的配置內(nèi)容,其中部分成員可以通過配置文件進(jìn)行配置。調(diào)用該中的函數(shù),該函數(shù)最終初始化模塊對應(yīng)的結(jié)構(gòu)體,完成配置。因此,分析源碼中的配置指令,就是分析結(jié)構(gòu)體。 本篇的上篇 Nginx 源碼分析:從模塊到配置(上),建議閱讀本篇前先閱讀上篇。 關(guān)于模塊 Nginx的架構(gòu)高度模塊化。每個(gè)模塊各司其職,組合在一...
摘要:四監(jiān)聽套接字的使用假設(shè)此處我們使用作為事件處理模塊在增加事件時(shí)用戶可以使用中的字段當(dāng)事件發(fā)生時(shí)該字段也會帶回。在創(chuàng)建監(jiān)聽套接字時(shí)將結(jié)構(gòu)分為級監(jiān)聽套接字地址各級都是一對多的關(guān)系。 施洪寶 一. 基礎(chǔ) nginx源碼采用1.15.5 后續(xù)部分僅討論http中的listen配置解析以及優(yōu)化流程 1.1 概述 假設(shè)nginx http模塊的配置如下 http{ server { ...
摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)??梢娝氖褂?,在開始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個(gè)名詞,今天我們首先來說說分布式。 探究...
摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)常可見它的使用,在開始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個(gè)名詞,今天我們首先來說說分布式。 探究...
摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)??梢娝氖褂?,在開始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個(gè)名詞,今天我們首先來說說分布式。 探究...
閱讀 2037·2021-11-12 10:36
閱讀 1904·2021-11-09 09:49
閱讀 2611·2021-11-04 16:12
閱讀 1157·2021-10-09 09:57
閱讀 3251·2019-08-29 17:24
閱讀 1924·2019-08-29 15:12
閱讀 1284·2019-08-29 14:07
閱讀 1298·2019-08-29 12:53