国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

不再依靠巧合編寫 Nginx 配置

tulayang / 1030人閱讀

摘要:找到這個模塊的指令后,則會調用這個指令的解析回調函數即結構體的第三個參數來進行處理。調用他們上面提到的中的回調函數來申請和初始化對應模塊的配置結構體。需要注意的是,即時當前是直接在塊級別,這三個回調函數都會被調用。拒絕暴力枚舉式編寫配置文件

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

熱身

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

第一個例子:

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

其中,echo指令來源于第三方模塊 echo ,作用是讓 Nginx 在接收到請求的時候將 echo 后面參數作為HTTP報文體進行返回。

第二個例子是:

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 分別會返回什么內容。

模塊化設計的Nginx

首先我們們嘗試一下使用官方的代碼構建一次Nginx。從Nginx官網下載最新的穩定版本1.14.0。執行:

./configure

可以發現,這一操作生成了 Makefile 文件和 objs 目錄,我們打開生成的其中一個非常關鍵的文件:objs/ngx_modules.c。可以看到,這個文件定義了兩個數組:

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

ngx_module_names 數組是上一數組成員一一對應的模塊的名字。

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

HTTP模塊內與配置相關的關鍵數據結構

由于HTTP模塊是Nginx中數量最多的模塊,我們日常寫配置文件是用的命令也大多屬于HTTP模塊,由于篇幅,我們就重點關注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
};

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

第一個參數是指令的名稱

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

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

第四個參數是指配置項所處內存的相對位置。這個描述會在稍后詳細說明。

第五個參數是配置項在整個存儲配置結構體中的偏移位置。

第六個參數使用較少,不做說明。

然后是 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 */
};

這個結構體的作用將在稍后說明。

配置文件解析

首先要對一些名詞進行說明:

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

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

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

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

解析的過程中,當碰到一個 http 指令的時候(其實一個也只能有一個http指令),該指令的解析回調函數會創建一個叫 ngx_http_conf_ctx_t 的結構體。這個結構體的定義如下:

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

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

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

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。當遇到 ssl 指令時,由于 ssl 指令的值是 on|off, 這個會被對應的將 ngx_http_ssl_srv_conf_t 的結構體中的 enable成員設置成1|0。conf 是當前級別(srv)下的指針,prev 是父級別(main)的指針。這段代碼的意思是,如果當前級別下沒有設置,則使用父級別的配置,如果父級別也沒有配置,則默認關閉。由此可見,并不一定所有指令的內層塊的配置都優先于外層塊的,具體采用哪個值取決于 merge 函數的編寫。

同理,在解析 srv 級別的配置的時候,每碰到一個 location 塊,這個指令的解析回調函數又會創建一個屬于這個 location 塊的 ngx_http_conf_ctx_t 結構體,他的 main_conf 和 loc_conf 都會指向父級 ngx_http_conf_ctx_t 結構體的 main_conf 和 loc_conf。解析完所有配置項后進行和父級配置的合并。至此,配置的解析完畢,最終會生成一個這樣的內存布局:

HTTP框架的執行流程

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

在 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

對于每一個請求的處理,都是必須經過這些階段的。在HTTP核心模塊里,有一個 ngx_http_core_main_conf_t 的結構體,里面有個成員是:

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 數組,數組的元素來源于各個模塊將自己的 handler 放到自己感興趣的階段的數組中來介入哥哥執行階段。通過該階段的 handlers 數組中 handler 的依次執行,來達到各個模塊間相互配合的目的。

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

回看例子

我們回頭來看看我們先前的例子,現在有頭緒了嗎?

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

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

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

結論

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

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/39936.html

相關文章

  • 企業云盤VS文檔管理系統:云計算的魅力與困惑

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

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

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

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

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

    chinafgj 評論0 收藏0
  • FastCGI+lighttpd開發之介紹和環境搭建

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

    gghyoo 評論0 收藏0

發表評論

0條評論

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