<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    Nginx 配置指令的執行順序(四)

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

     ngx_lua 模塊提供了配置指令 access_by_lua,用于在 access 請求處理階段插入用戶 Lua 代碼。這條指令運行于 access 階段的末尾,因此總是在 allow 和 deny 這樣的指令之后運行,雖然它們同屬 access 階段。一般我們通過 access_by_lua 在 ngx_access 這樣的模塊檢查過客戶端 IP 地址之后,再通過 Lua 代碼執行一系列更為復雜的請求驗證操作,比如實時查詢數據庫或者其他后端服務,以驗證當前用戶的身份或權限。

     

        我們來看一個簡單的例子,利用 access_by_lua 來實現 ngx_access 模塊的 IP 地址過濾功能:

        location /hello {
            access_by_lua '
                if ngx.var.remote_addr == "127.0.0.1" then
                    return
                end
     
                ngx.exit(403)
            ';
     
            echo "hello world";
        }

    這里在 Lua 代碼中通過引用 Nginx 標準的內建變量 $remote_addr 來獲取字符串形式的客戶端 IP 地址,然后用 Lua 的 if 語句判斷是否為本機地址,即是否等于 127.0.0.1. 如果是本機地址,則直接利用 Lua 的return 語句返回,讓 Nginx 繼續執行后續的請求處理階段(包括 echo 指令所處的 content 階段);而如果不是本機地址,則通過 ngx_lua 模塊提供的 Lua 函數 ngx.exit 中斷當前的整個請求處理流程,直接返回 403錯誤頁給客戶端。

     

        這個例子在功能上完全等價于先前在 (三) 中介紹過的那個使用 ngx_access 模塊的例子:

        location /hello {
            allow 127.0.0.1;
            deny all;
     
            echo "hello world";
        }

    雖然這兩個例子在功能上完全相同,但在性能上還是有區別的,畢竟 ngx_access 是用純 C 實現的專門化的 Nginx 模塊。

     

        下面我們不妨來實際測量一下這兩個例子的性能差別。因為我們使用 Nginx 就是為了追求性能,而量化的性能比較,在工程上具有很大的現實意義,所以我們順便介紹一下重要的測量技術。由于無論是 ngx_access 還是 ngx_lua 在進行 IP 地址驗證方面的性能都非常之高,所以為了減少測量誤差,我們希望能對 access 階段的用時進行直接測量。為了做到這一點,傳統的做法一般會涉及到修改 Nginx 源碼,自己插入專門的計時代碼和統計輸出代碼,抑或是重新編譯 Nginx 以啟用像 GNU gprof 這樣專門的性能監測工具。

     

        幸運的是,在新一點的 Solaris, Mac OS X, 以及 FreeBSD 等系統上存在一個叫做 dtrace 的工具,可以對任意的用戶程序進行微觀性能分析(以及行為分析),而無須對用戶程序的源碼進行修改或者對用戶程序進行重新編譯。因為 Mac OS X 10.5 以后就自帶了 dtrace,所以為方便起見,下面在我的 MacBook Air 筆記本上演示一下這里的測量過程。

     

        首先,在 Mac OS X 系統中打開一個命令行終端,在某一個文件目錄下面創建一個名為 nginx-access-time.d 的文件,并編輯內容如下:

        #!/usr/bin/env dtrace -s
     
        pid$1::ngx_http_handler:entry
        {
            elapsed = 0;
        }
     
        pid$1::ngx_http_core_access_phase:entry
        {
            begin = timestamp;
        }
     
        pid$1::ngx_http_core_access_phase:return
        /begin > 0/
        {
            elapsed += timestamp - begin;
            begin = 0;
        }
     
        pid$1::ngx_http_finalize_request:return
        /elapsed > 0/
        {
            @elapsed = avg(elapsed);
            elapsed = 0;
        }

    保存好此文件后,再賦予它可執行權限:

        $ chmod +x ./nginx-access-time.d

    這個 .d 文件中的代碼是用 dtrace 工具自己提供的 D 語言來編寫的(注意,這里的 D 語言并不同于 Walter Bright 作為另一種“更好的 C++”而設計的 D 語言)。由于本系列教程并不打算介紹如何編寫 dtrace 的 D腳本,同時理解這個腳本需要不少有關 Nginx 內部源碼實現的細節,所以這里我們不展開介紹。大家只需要知道這個腳本的功能是:統計指定的 Nginx worker 進程在處理每個請求時,平均花費在 access 階段上的時間。

     

        現在來演示一下這個 D 腳本的運行方法。這個腳本接受一個命令行參數用于指定監視的 Nginx worker 進程的進程號(pid)。由于 Nginx 支持多 worker 進程,所以我們測試時發起的 HTTP 請求可能由其中任意一個 worker 進程服務。為了確保所有測試請求都為固定的 worker 進程處理,不妨在 nginx.conf 配置文件中指定只啟用一個 worker 進程:

        worker_processes 1;

    重啟 Nginx 服務器之后,可以利用 ps 命令得到當前 worker 進程的進程號:

        $ ps ax|grep nginx|grep worker|grep -v grep

    在我機器上的一次典型輸出是

        10975   ??  S      0:34.28 nginx: worker process

    其中第一列的數值便是我的 nginx worker 進程的進程號,10975。如果你得到的輸出不止一行,則通常意味著你的系統中同時運行著多個 Nginx 服務器實例,或者當前 Nginx 實例啟用了多個 worker 進程。

     

        接下來使用剛剛得到的 worker 進程號以及 root 身份來運行 nginx-access-time.d 腳本:

        $ sudo ./nginx-access-time.d 10975

    如果一切正常,則會看到這樣一行輸出:

        dtrace: script './nginx-access-time.d' matched 4 probes

    這行輸出是說,我們的 D 腳本已成功向目標進程動態植入了 4 個 dtrace “探針”(probe)。緊接著這個腳本就掛起了,表明 dtrace 工具正在對進程 10975 進行持續監視。

     

        然后我們再打開一個新終端,在那里使用 curl 這樣的工具多次請求我們正在監視的接口

        $ curl 'http://localhost:8080/hello'
        hello world
     
        $ curl 'http://localhost:8080/hello'
        hello world

    最后我們回到原先那個一直在運行 D 腳本的終端,按下 Ctrl-C 組合鍵中止 dtrace 的運行。而該腳本在退出時會向終端打印出最終統計結果。例如我的終端此時是這個樣子的:

        $ sudo ./nginx-access-time.d 10975
        dtrace: script './nginx-access-time.d' matched 4 probes
        ^C
               19219

    最后一行輸出 19219 便是那幾次 curl 請求在 access 階段的平均用時(以納秒,即 10 的負 9 次方秒為單位)。

     

        通過上面介紹的步驟,可以通過 nginx-access-time.d 腳本分別統計出各種不同的 Nginx 配置下 access階段的平均用時。針對我們感興趣的三種情況可以進行三組平行試驗,即使用 ngx_access 過濾 IP 地址的情況,使用 access_by_lua 過濾 IP 地址的情況,以及不在 access 階段使用任何配置指令的情況。最后一種情況屬于“空白對照組”,用于校正測試過程中因 dtrace 探針等其他因素而引入的“系統誤差”。另外,為了最小化各種不可控的“隨機誤差”,可以用 ab 這樣的批量測試工具來取代 curl 發起連續十萬次以上的請求,例如

        $ ab -k -c1 -n100000 'http://127.0.0.1:8080/hello'

    這樣我們的 D 腳本統計出來的平均值將更加接近“真實值”。

     

        在我的蘋果系統上,一次典型的測試結果如下:

        ngx_access 組               18146
        access_by_lua 組            35011
        空白對照組                   15887

    把前兩組的結果分別減去“空白對照組”的結果可以得到

        ngx_access 組               2259
        access_by_lua 組           19124

    可以看到,ngx_access 組比 access_by_lua 組快了大約一個數量級,這正是我們所預期的。不過其絕對時間差是極小的,對于我的 Intel Core2Duo 1.86 GHz 的 CPU 而言,也只有區區十幾微秒,或者說是在十萬分之一秒的量級。

     

        當然,上面使用 access_by_lua 的例子還可以通過換用 $binary_remote_addr 內建變量進行優化,因為$binary_remote_addr 讀出的是二進制形式的 IP 地址,而 $remote_addr 則返回更長一些的字符串形式的地址。更短的地址意味著用 Lua 進行字符串比較時通常可以更快。

     

        值得注意的是,如果按 (一) 中介紹的方法為 Nginx 開啟了“調試日志”的話,上面統計出來的時間會顯著增加,因為“調試日志”自身的開銷是很大的。

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全