<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系統、金蝶與條碼系統對接、用友與條碼系統對接

    前面在 (二) 中我們已經了解到變量值容器的生命期是與請求綁定的,但是我當時有意避開了“請求”的正式定義。大家應當一直默認這里的“請求”都是指客戶端發起的 HTTP 請求。其實在 Nginx 世界里有兩種類型的“請求”,一種叫做“主請求”(main request),而另一種則叫做“子請求”(subrequest)。我們先來介紹一下它們。

     

        所謂“主請求”,就是由 HTTP 客戶端從 Nginx 外部發起的請求。我們前面見到的所有例子都只涉及到“主請求”,包括 (二) 中那兩個使用 echo_exec 和 rewrite 指令發起“內部跳轉”的例子。

     

        而“子請求”則是由 Nginx 正在處理的請求在 Nginx 內部發起的一種級聯請求。“子請求”在外觀上很像 HTTP 請求,但實現上卻和 HTTP 協議乃至網絡通信一點兒關系都沒有。它是 Nginx 內部的一種抽象調用,目的是為了方便用戶把“主請求”的任務分解為多個較小粒度的“內部請求”,并發或串行地訪問多個 location 接口,然后由這些 location 接口通力協作,共同完成整個“主請求”。當然,“子請求”的概念是相對的,任何一個“子請求”也可以再發起更多的“子子請求”,甚至可以玩遞歸調用(即自己調用自己)。當一個請求發起一個“子請求”的時候,按照 Nginx 的術語,習慣把前者稱為后者的“父請求”(parent request)。值得一提的是,Apache 服務器中其實也有“子請求”的概念,所以來自 Apache 世界的讀者對此應當不會感到陌生。

     

        下面就來看一個使用了“子請求”的例子:

        location /main {
            echo_location /foo;
            echo_location /bar;
        }
     
        location /foo {
            echo foo;
        }
     
        location /bar {
            echo bar;
        }

    這里在 location /main 中,通過第三方 ngx_echo 模塊的 echo_location 指令分別發起到 /foo 和 /bar 這兩個接口的 GET 類型的“子請求”。由 echo_location 發起的“子請求”,其執行是按照配置書寫的順序串行處理的,即只有當 /foo 請求處理完畢之后,才會接著處理 /bar 請求。這兩個“子請求”的輸出會按執行順序拼接起來,作為 /main 接口的最終輸出:

        $ curl 'http://localhost:8080/main'
        foo
        bar

    我們看到,“子請求”方式的通信是在同一個虛擬主機內部進行的,所以 Nginx 核心在實現“子請求”的時候,就只調用了若干個 C 函數,完全不涉及任何網絡或者 UNIX 套接字(socket)通信。我們由此可以看出“子請求”的執行效率是極高的。

     

        回到先前對 Nginx 變量值容器的生命期的討論,我們現在依舊可以說,它們的生命期是與當前請求相關聯的。每個請求都有所有變量值容器的獨立副本,只不過當前請求既可以是“主請求”,也可以是“子請求”。即便是父子請求之間,同名變量一般也不會相互干擾。讓我們來通過一個小實驗證明一下這個說法:

        location /main {
            set $var main;
     
            echo_location /foo;
            echo_location /bar;
     
            echo "main: $var";
        }
     
        location /foo {
            set $var foo;
            echo "foo: $var";
        }
     
        location /bar {
            set $var bar;
            echo "bar: $var";
        }

    在這個例子中,我們分別在 /main/foo 和 /bar 這三個 location 配置塊中為同一名字的變量,$var,分別設置了不同的值并予以輸出。特別地,我們在 /main 接口中,故意在調用過 /foo 和 /bar 這兩個“子請求”之后,再輸出它自己的 $var 變量的值。請求 /main 接口的結果是這樣的:

        $ curl 'http://localhost:8080/main'
        foo: foo
        bar: bar
        main: main

    顯然,/foo 和 /bar 這兩個“子請求”在處理過程中對變量 $var 各自所做的修改都絲毫沒有影響到“主請求” /main. 于是這成功印證了“主請求”以及各個“子請求”都擁有不同的變量 $var 的值容器副本。

     

        不幸的是,一些 Nginx 模塊發起的“子請求”卻會自動共享其“父請求”的變量值容器,比如第三方模塊ngx_auth_request. 下面是一個例子:

        location /main {
            set $var main;
            auth_request /sub;
            echo "main: $var";
        }
     
        location /sub {
            set $var sub;
            echo "sub: $var";
        }

    這里我們在 /main 接口中先為 $var 變量賦初值 main,然后使用 ngx_auth_request 模塊提供的配置指令auth_request,發起一個到 /sub 接口的“子請求”,最后利用 echo 指令輸出變量 $var 的值。而我們在/sub 接口中則故意把 $var 變量的值改寫成 sub. 訪問 /main 接口的結果如下:

        $ curl 'http://localhost:8080/main'
        main: sub

    我們看到,/sub 接口對 $var 變量值的修改影響到了主請求 /main. 所以 ngx_auth_request 模塊發起的“子請求”確實是與其“父請求”共享一套 Nginx 變量的值容器。

     

        對于上面這個例子,相信有讀者會問:“為什么‘子請求’ /sub 的輸出沒有出現在最終的輸出里呢?”答案很簡單,那就是因為 auth_request 指令會自動忽略“子請求”的響應體,而只檢查“子請求”的響應狀態碼。當狀態碼是 2XX 的時候,auth_request 指令會忽略“子請求”而讓 Nginx 繼續處理當前的請求,否則它就會立即中斷當前(主)請求的執行,返回相應的出錯頁。在我們的例子中,/sub “子請求”只是使用 echo指令作了一些輸出,所以隱式地返回了指示正常的 200 狀態碼。

     

        如 ngx_auth_request 模塊這樣父子請求共享一套 Nginx 變量的行為,雖然可以讓父子請求之間的數據雙向傳遞變得極為容易,但是對于足夠復雜的配置,卻也經常導致不少難于調試的詭異 bug. 因為用戶時常不知道“父請求”的某個 Nginx 變量的值,其實已經在它的某個“子請求”中被意外修改了。諸如此類的因共享而導致的不好的“副作用”,讓包括 ngx_echongx_lua,以及 ngx_srcache 在內的許多第三方模塊都選擇了禁用父子請求間的變量共享。

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