成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

不再依靠巧合編寫 Nginx 配置

tulayang / 1027人閱讀

摘要:找到這個模塊的指令后,則會調(diào)用這個指令的解析回調(diào)函數(shù)即結(jié)構(gòu)體的第三個參數(shù)來進(jìn)行處理。調(diào)用他們上面提到的中的回調(diào)函數(shù)來申請和初始化對應(yīng)模塊的配置結(jié)構(gòu)體。需要注意的是,即時當(dāng)前是直接在塊級別,這三個回調(diào)函數(shù)都會被調(diào)用。拒絕暴力枚舉式編寫配置文件

原博:https://blog.coordinate35.cn/...

熱身

首先來看下這幾個小例子:

第一個例子:

server {
    listen 80;
    root /var/www/html;
    index index.html;
    
    location /test {
        root /var/www/demo
    }
}

其中,echo指令來源于第三方模塊 echo ,作用是讓 Nginx 在接收到請求的時候?qū)?echo 后面參數(shù)作為HTTP報文體進(jìn)行返回。

第二個例子是:

location /test {
    set $a 32;
    echo $a;
    set $a 56;
    echo $a;
}

第三個例子是:

location /test {
    echo hello;
    content_by_lua "ngx.say("world")";
}

大家可以想一下,假定所有可能需要的資源都存在,如果 Nginx 收到 /test 的請求,這三種情況下 Nginx 分別會返回什么內(nèi)容。

模塊化設(shè)計的Nginx

首先我們們嘗試一下使用官方的代碼構(gòu)建一次Nginx。從Nginx官網(wǎng)下載最新的穩(wěn)定版本1.14.0。執(zhí)行:

./configure

可以發(fā)現(xiàn),這一操作生成了 Makefile 文件和 objs 目錄,我們打開生成的其中一個非常關(guān)鍵的文件:objs/ngx_modules.c??梢钥吹?,這個文件定義了兩個數(shù)組:

ngx_modules 數(shù)組的成員是 Nginx 所有需要使用的模塊的對象的指針。

ngx_module_names 數(shù)組是上一數(shù)組成員一一對應(yīng)的模塊的名字。

從這個文件基本上可以窺探出,除了少量核心代碼,其余Nginx的代碼是由一個個這樣的模塊構(gòu)成的。需要特別說明的是,這個數(shù)組里面各個模塊的先后順序特別重要。這個先后順序代表了在Nginx中模塊的優(yōu)先級,當(dāng)兩個模塊的功能有重疊的時候,通過在數(shù)組里面的先后順序來決定使用哪個模塊的邏輯。事實上,Nginx有五大類型的模塊:核心模塊、配置模塊、事件模塊、HTTP模塊、mail模塊。

HTTP模塊內(nèi)與配置相關(guān)的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)

由于HTTP模塊是Nginx中數(shù)量最多的模塊,我們?nèi)粘懪渲梦募怯玫拿钜泊蠖鄬儆贖TTP模塊,由于篇幅,我們就重點關(guān)注HTTP類型的模塊。

首先是 ngx_command_t 類型,定義舉例:

static ngx_command_t ngx_http_gzip_filter_commands[] = {
    { ngx_string("gzip"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
                        |NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_gzip_conf_t, enable),
      NULL },
    ...,
    ngx_null_command
};

這是一個數(shù)組,存放了這個模塊里可用的所有指令。對于數(shù)組的每一個元素,

第一個參數(shù)是指令的名稱

第二個參數(shù)是有關(guān)于這個指令的類型描述:指令是在http塊出現(xiàn),還是server塊出現(xiàn),還是在location塊出現(xiàn)?這個指令之后跟多少個參數(shù)?參數(shù)的類型是什么,數(shù)值還是一個配置塊。

第三個參數(shù)是一個函數(shù)指針,這個函數(shù)用于解析指令后的參數(shù)。第四個參數(shù)是

第四個參數(shù)是指配置項所處內(nèi)存的相對位置。這個描述會在稍后詳細(xì)說明。

第五個參數(shù)是配置項在整個存儲配置結(jié)構(gòu)體中的偏移位置。

第六個參數(shù)使用較少,不做說明。

然后是 ngx_http_module_t 類型

static ngx_http_module_t  ngx_http_gzip_filter_module_ctx = {
    ngx_http_gzip_add_variables,           /* preconfiguration */
    ngx_http_gzip_filter_init,             /* postconfiguration */

    NULL,                                  /* create_main_conf */
    NULL,                                  /* init_main_conf */

    NULL,                                  /* create_srv_conf */
    NULL,                                  /* merge_srv_conf */

    ngx_http_gzip_create_conf,             /* create_loc_conf */
    ngx_http_gzip_merge_conf               /* merge_srv_conf */
};

這個結(jié)構(gòu)體的作用將在稍后說明。

配置文件解析

首先要對一些名詞進(jìn)行說明:

直接在 http{} 下的配置叫 main 配置項

直接在 server{} 下的配置叫 srv 配置項

直接在 location{} 下的配置叫 loc 配置項

在Nginx解析配置文件的時候,會調(diào)用 ngx_conf_parse 這個函數(shù)進(jìn)行配置文件解析。首先應(yīng)該清楚地認(rèn)識到,Nginx 的配置文件實際上就是由指令和指令參數(shù)組成的。ngx_conf_parse首先會將配置文件進(jìn)行詞法分析,將配置文件生成一個指令數(shù)組,數(shù)組的每一個元素也都是一個字符串?dāng)?shù)組,成員數(shù)組的第一個元素是解析出來的指令名字,之后的參數(shù)是配置文件里這個指令的參數(shù)列表。然后,ngx_conf_parse 會遍歷這個指令數(shù)組,對于每一個指令,Nginx會遍歷一次所有的模塊,直到發(fā)現(xiàn)第一個,指令出現(xiàn)位置和參數(shù)要求都符合要求的模塊(也就是之前提到的ngx_command_t數(shù)組元素的第二條配置。這也意味著,如果有兩個模塊都定義了同一個指令的名字,參數(shù)和出現(xiàn)的位置都符合要求,Nginx會選擇使用在上面提到的 ngx_module_t* 數(shù)組排的靠前的那個模塊,因為先遍歷到)。找到這個模塊的指令后,則會調(diào)用這個指令的解析回調(diào)函數(shù)(即 ngx_command_t 結(jié)構(gòu)體的第三個參數(shù))來進(jìn)行處理。如果該指令是一個用{}包圍的配置塊,則會遞歸地調(diào)用 ngx_conf_parse 來進(jìn)行配置文件解析。

解析的過程中,當(dāng)碰到一個 http 指令的時候(其實一個也只能有一個http指令),該指令的解析回調(diào)函數(shù)會創(chuàng)建一個叫 ngx_http_conf_ctx_t 的結(jié)構(gòu)體。這個結(jié)構(gòu)體的定義如下:

typedef struct {
    void **main_conf;
    void **srv_conf;
    void **loc_conf;
} ngx_http_conf_ctx_t;

結(jié)構(gòu)體中,兩個星代表這個參數(shù)是一個指針數(shù)組。然后根據(jù)HTTP模塊的數(shù)量,建立長度相匹配 main_conf、srv_conf、loc_conf 數(shù)組。接著,依次遍歷各個HTTP模塊。調(diào)用他們 ngx_http_module_t(上面提到的) 中的 create_main_conf、create_srv_conf、create_loc_conf 回調(diào)函數(shù)來申請和初始化對應(yīng)模塊的配置結(jié)構(gòu)體。也就是說main_conf、srv_conf、loc_conf數(shù)組中下標(biāo)為n的元素,都對應(yīng)著第 n+1 個HTTP模塊配置結(jié)構(gòu)體。需要注意的是,即時當(dāng)前是直接在 http 塊(main級別),create_main_conf、create_srv_conf、create_loc_conf 這三個回調(diào)函數(shù)都會被調(diào)用。具體原因會稍后說明。

做完上述步驟后,Nginx 會遞歸地調(diào)用 ngx_conf_parse 來解析 之后 {} 中的配置項,在這個過程中,每碰到一個 server 指令的之后,這個指令的解析回調(diào)函數(shù)又會創(chuàng)建一個屬于這個 server 塊的 ngx_http_conf_ctx_t 結(jié)構(gòu)體。唯一不同的就是,這個結(jié)構(gòu)體的 main_conf 會指向他的父 http 塊的 main_conf 數(shù)組(顯而易見,在srv 級別的配置里,main級別的配置是不會發(fā)生變化的)。在解析 srv 級別的配置中,如果有同一個模塊的同一個指令既出現(xiàn)在了 main 級別的塊下,又出現(xiàn)在了 srv 級別的塊下,應(yīng)該以哪一個為準(zhǔn)呢?這就輪到我們的merge函數(shù)大顯身手,同時這也解釋了為什么不管在什么級別下,都要為每個模塊生成 main_conf、srv_conf、loc_conf。這是因為有些配置項可以同時出現(xiàn)在 http{} server{} location{} 中。這樣我們就會把只能在 http{} 出現(xiàn)的指令放在各模塊的 main_conf 結(jié)構(gòu)體里面,把只能出現(xiàn)在 http{} server{} 的配置項放在 srv_conf 結(jié)構(gòu)體里面,把在 http{} server{} location{} 都能出現(xiàn)的配置項就放在 loc_conf 結(jié)構(gòu)體里面。在我們遍歷到 srv 級別這種情況,比如 ssl 指令。這時就會調(diào)用 ngx_http_ssl_module 模塊的 ngx_http_module_t 結(jié)構(gòu)體(上面有提到) merge_srv_conf 回調(diào)函數(shù)來進(jìn)行合并。在 ssl 模塊的 merge_srv_conf 函數(shù)中的某一段代碼如下:

if (conf->enable == NGX_CONF_UNSET) {
    if (prev->enable == NGX_CONF_UNSET) {
        conf->enable = 0;

    } else {
        conf->enable = prev->enable;
        conf->file = prev->file;
        conf->line = prev->line;
    }
}

這里, conf 和 prev 的類型都是ngx_http_ssl_srv_conf_t。當(dāng)遇到 ssl 指令時,由于 ssl 指令的值是 on|off, 這個會被對應(yīng)的將 ngx_http_ssl_srv_conf_t 的結(jié)構(gòu)體中的 enable成員設(shè)置成1|0。conf 是當(dāng)前級別(srv)下的指針,prev 是父級別(main)的指針。這段代碼的意思是,如果當(dāng)前級別下沒有設(shè)置,則使用父級別的配置,如果父級別也沒有配置,則默認(rèn)關(guān)閉。由此可見,并不一定所有指令的內(nèi)層塊的配置都優(yōu)先于外層塊的,具體采用哪個值取決于 merge 函數(shù)的編寫。

同理,在解析 srv 級別的配置的時候,每碰到一個 location 塊,這個指令的解析回調(diào)函數(shù)又會創(chuàng)建一個屬于這個 location 塊的 ngx_http_conf_ctx_t 結(jié)構(gòu)體,他的 main_conf 和 loc_conf 都會指向父級 ngx_http_conf_ctx_t 結(jié)構(gòu)體的 main_conf 和 loc_conf。解析完所有配置項后進(jìn)行和父級配置的合并。至此,配置的解析完畢,最終會生成一個這樣的內(nèi)存布局:

HTTP框架的執(zhí)行流程

配置文件所有解析完了之后 ,Nginx才正式開始fork出 worker 進(jìn)程,接收請求的處理。

在 Nginx 中,對 HTTP 請求的處理被劃分成了11個處理階段:

NGX_HTTP_POST_READ_PHASE

NGX_HTTP_SERVER_REWRITE_PHASE

NGX_HTTP_FIND_CONF_PHASE

NGX_HTTP_REWRITE_PHASE

NGX_HTTP_POST_REWRITE_PHASE

NGX_HTTP_PREACCESS_PHASE

NGX_HTTP_ACCESS_PHASE

NGX_HTTP_POST_ACCESS_PHASE

NGX_HTTP_TRY_FILES_PHASE

NGX_HTTP_CONTENT_PHASE

NGX_HTTP_LOG_PHASE

對于每一個請求的處理,都是必須經(jīng)過這些階段的。在HTTP核心模塊里,有一個 ngx_http_core_main_conf_t 的結(jié)構(gòu)體,里面有個成員是:

ngx_http_phase_t phase[NGX_HTTP_LOG_PHASE + 1];

而 ngx_http_phase_t 的定義如下:

typedef struct {
    ngx_array_t handlers;
} ngx_http_phase_t;

也就是說,原則上,每個階段都有一個自己的 handlers 數(shù)組,數(shù)組的元素來源于各個模塊將自己的 handler 放到自己感興趣的階段的數(shù)組中來介入哥哥執(zhí)行階段。通過該階段的 handlers 數(shù)組中 handler 的依次執(zhí)行,來達(dá)到各個模塊間相互配合的目的。

但是 NGX_HTTP_CONTENT_PHASE 階段,也就是響應(yīng)內(nèi)容生成的階段則稍有例外,而這個階段也是大多數(shù)模塊介入的階段。要介入這個階段,不僅可以通過往 handlers 數(shù)組添加 handler 的方式,還可以通過設(shè)置 ngx_http_core_loc_conf_t 中的 handler 指針來實現(xiàn)。通過這種方式,handlers數(shù)組的handler就會全部被屏蔽掉,而只有這個handler生效。顯然,如果有兩個模塊都嘗試去通過這種方式介入 NGX_HTTP_CONTENT_PHASE 階段,必然只有一個能生效。

回看例子

我們回頭來看看我們先前的例子,現(xiàn)在有頭緒了嗎?

對于第一個例子,root 的配置在 merge 的過程中,使用了 loc 級別的配置。不過可能還是得注意不一定永遠(yuǎn)都會這樣。

對于第二個例子,我們可以看到 set 指令是在加載配置的過程中將變量設(shè)置好的。在進(jìn)行 HTTP 請求處理的時候,變量 $a 的值已經(jīng)被覆蓋過一次了,所以返回的結(jié)果是兩個64.這說明配置通常不是按直覺上的從上而下執(zhí)行的,一定要結(jié)合整個 Nginx 的配置加載-請求處理的原理進(jìn)行考慮。

對于第三個例子,通過閱讀代碼,我們知道 echo 指令和 content_by_lua 都是通過設(shè)置 ngx_http_core_main_conf_t 的 handler 成員來介入 NGX_HTTP_CONTENT_PHASE 階段的,所以只有一個會生效,具體哪個指令會生效,取決于這兩個指令所在模塊的在 ngx_modules 數(shù)組的先后位置。

結(jié)論

Nginx 的配置很多時候會和我們所想的有所出入,同時它又時候也不是那么直觀明了。當(dāng)踩到坑的時候,一定要多查看文檔,結(jié)合 Nginx 的原理進(jìn)行分析。甚至是去閱讀指令所在模塊的代碼(主要是配置合并函數(shù)和模塊介入各個階段的方式),然后去有理有據(jù)的書寫配置。拒絕暴力枚舉式編寫配置文件!

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/39936.html

相關(guān)文章

  • 企業(yè)云盤VS文檔管理系統(tǒng):云計算的魅力與困惑

    摘要:在實現(xiàn)模式上,公有云與文檔管理系統(tǒng)不同,它們的服務(wù)器要處理所有客戶的各類數(shù)據(jù),為節(jié)省成本,計算資源必須小心分配,一些消耗資源較多的操作會做出限制。云計算是當(dāng)前IT市場上一個炙手可熱的概念,當(dāng)然這已經(jīng)不僅僅局限于概念,它事實上已成為強有力的商業(yè)模式,促成了微軟在2018年未重奪全球市值最高寶座,巧合的是同樣主推云計算的亞馬遜正好是全球市值第二的公司。云計算究竟有何魅力,它是否能更好滿足客戶需求...

    Channe 評論0 收藏0
  • Kubernetes 之所以酷

    摘要:比如,現(xiàn)在我們集群中的控制器就有內(nèi)存泄漏的問題,調(diào)度器經(jīng)常崩潰。例如,你的控制管理組件有內(nèi)存泄漏的問題,由于控制管理組件是無狀態(tài)的,你能夠間歇的重啟它,比如每小時一次,并且完全不會產(chǎn)生其他不好的連鎖反應(yīng)。 Kubernetes 之所以酷 來自我的博客小站 Level Up 前言 當(dāng)我最開始了解到 Kubernetes 的時候(大概一年半以前?),我真的找不出需要關(guān)注它的理由。 滿打滿算...

    raoyi 評論0 收藏0
  • 基于Cloud Foundry的PaaS開發(fā)與部署

    摘要:基于的實踐一綜述參見還可參見基于的實踐之集群部署單結(jié)點的部署由于提供的安裝腳本,使用簡單不再陳述,大家參照一下官網(wǎng)即可,在此主要談?wù)劧嘟Y(jié)點集群部署的要點。關(guān)于,大家可參考和。 使用Cloud foundry(以下簡稱CF)接近一年時間,一直缺少時間寫些東西與大家探討。最近,打算寫一下。目前使用經(jīng)歷主要包括:  1. 搭建CF運行環(huán)境并維護;  2. 部分代碼修改和新功能擴充的工作;  3. ...

    chinafgj 評論0 收藏0
  • FastCGI+lighttpd開發(fā)之介紹和環(huán)境搭建

    摘要:原文開發(fā)之介紹和環(huán)境搭建由于需要做一些簡單的基于的開發(fā),開始學(xué)習(xí)和調(diào)研,本篇介紹和的概念以及基于官方的,以及搭建起簡單的開發(fā)環(huán)境,以作備忘。所以程序其實沒有語言限制,只要能夠讀取環(huán)境變量,讀寫標(biāo)準(zhǔn)輸入輸出即可。管理器有多種形式。 原文:FastCGI+lighttpd開發(fā)之介紹和環(huán)境搭建 由于需要做一些簡單的基于FastCGI的Web開發(fā),開始學(xué)習(xí)和調(diào)研,本篇介紹CGI和FastCGI...

    gghyoo 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<