亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

首頁 慕課教程 Nginx 入門教程 Nginx 入門教程 10 Nginx 的 Http 模塊介紹(上)

Nginx 的 Http 模塊介紹(上)

本部分內容將詳細介紹 Nginx 中對 Http請求的 11 個處理階段,分成 3 個小節講解并進行相關實驗操作。

1. http 請求 11 個處理階段介紹

Nginx 將一個 Http 請求分成多個階段,以模塊為單位進行處理。其將 Http請求的處理過程分成了 11 個階段,各個階段可以包含任意多個 Http 的模塊并以流水線的方式處理請求。這 11 個 Http 階段如下所示:

typedef enum {
    NGX_HTTP_POST_READ_PHASE = 0,   
    NGX_HTTP_SERVER_REWRITE_PHASE,  

    NGX_HTTP_FIND_CONFIG_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              
} ngx_http_phases;

網上有人做了一個非常形象的圖片,如下圖所示。我們可以看到 11 個階段的處理順序,以及每個階段中涉及到的相關模塊以及模塊之間的順序。

圖片描述

1.1 POST_READ 階段

POST_READ 階段是 Nginx 接收到 Http 請求完整頭部后的處理階段,這里主要使用的是 realip 模塊獲取用戶的真實地址,方便后續對該 IP 進行限速或者過濾其請求等。

1.2 SERVER_REWRITE 和 REWRITE 階段

SERVER_REWRITE 和后面的 REWRITE 階段一般是使用 rewrite 模塊修改 Http請求的 uri,實現請求的控制。

1.3 FIND_CONFIG 階段

FIND_CONFIG 階段只是做 location 的匹配項。

1.4 PREACCESS、ACCESS 和 POST_ACCESS 階段

PREACCESS、ACCESS 和 POST_ACCESS 是和 Http 請求訪問權限相關的階段。PREACCESS 階段是在連接之前要做的訪問控制, 這個階段有 limit_conn 和 limit_req 等模塊工作。ACCESS 階段是解決用戶能不能訪問,比如根據用戶名、密碼限制用戶訪問(auth_basic 模塊)、根據 ip 限制用戶訪問(access 模塊)以及第三方模塊認證限制用戶的訪問(auth_request模塊)。POST_ACCESS 是在 ACCESS 之后要做的一些工作。

1.5 TRY_FILES 階段

TRY_FILES 階段為訪問靜態文件資源而設置的。有時候又稱之為 PRECONTENT 階段,即在 CONTENT 階段之前做的事情。主要是 try_files 模塊在此階段工作。

1.6 CONTENT

最重要的 CONTENT 是處理 Http 請求內容的階段,大部分 HTTP 模塊介入這個階段,比如 index、autoindex、concat 以及反向代理的模塊都是在這里生效的。

1.7 LOG 階段

LOG 是處理完請求后的日志記錄階段,如 access_log 模塊。

Tips: 所有的 Http請求必須都是從上到下,一個接一個階段執行的。

2. realip 模塊

realip 模塊是在 postread 階段生效的,它的作用是:當本機的 nginx 處于一個反向代理的后端時獲取到真實的用戶 ip。 如果沒有 realip 模塊,Nginx 中的 $remote_addr 可能就不是客戶端的真實 ip 了,而是代理主機的 ip。
realip模塊的配置實例如下:

   set_real_ip_from 10.10.10.10;
   # real_ip_recursive off;
   real_ip_recursive on;
   real_ip_header X-Forwarded-For;

set_real_ip_from 是指定我們信任的后端代理服務器,real_ip_header 是告訴 nginx 真正的用戶 ip 是存在 X-Forwarded-For 請求頭中的。

當 real_ip_recursive 設置為 off 時,nginx 會把 real_ip_header 指定的 Http頭中的最后一個 ip 當成真實 ip;

而當 real_ip_recursive 為 on 時,nginx 會把 real_ip_header 指定的 Http頭中的最后一個不是信任服務器的 ip (前面設置的set_real_ip_from)當成真實 ip。通過這樣的手段,最后拿到用戶的真實 ip。

3. rewrite 模塊

rewrite 模塊可以看到它在 SERVER_REWRITE 和 REWRITE 階段都有介入。rewrite 模塊的主要功能是改寫請求的 uri。它是 Nginx 默認安裝的模塊。rewrite 模塊會根據正則匹配重寫 uri,然后發起內部跳轉再匹配 location, 或者直接做30x重定向返回客戶端。rewrite 模塊的指令有 break, if, return, rewrite, set 等,這些都是我們常用到的。

3.1 return 指令

Syntax:	return code [text];
# return code URL;
# return URL;
Default:Context: server, location, if

return 指令返回后,Http 請求將在 return 的階段終止,后續階段將無法進行,所以許多模塊得不到執行。

return 200 "hello, world"

3.2 rewrite 指令

Syntax:  rewrite regex replacement [flag];
Default: --
Context: server, location, if

1、將 regex 指定的 url 替換成 replacement 這個新的 url,可以使用正則表達式及變量提取。

2、當 replacement 以 http:// 或者 https:// 或者 $schema 開頭,則直接返回 302 重定向

3、替換后的 url 根據 flag 指定的方式進行處理

  • last: 用 replacement 這個 url 進行新的 location 匹配
  • break: break 指令停止當前腳本指令的執行
  • redirect:返回 302 重定向
  • permanent: 返回 301 重定向

3.3 if 指令

Syntax:	 if (condition) { ... }
Default: —
Context: server, location

if 指令的條件表達式:

  • 檢查變量是否為空或者為 0
  • 將變量與字符串做匹配,使用 = 或者 !=
  • 將變量與正則表達式做匹配:
    • ~ 或者 !~ 大小寫敏感
    • ~* 或者 !~* 大小寫不敏感
  • 檢查文件是否存在 -f 或者 !-f
  • 檢查目錄是否存在 -d 或者 !-d
  • 檢查文件、目錄、軟鏈接是否存在 -e !-e
  • 是否為可執行文件 -x 或者 !-x

實例

if ($request_medthod = POST){
   return 405;
}

if($invalid_refer){
   return 403;
}

4. location 匹配

location 匹配是在 FIND_CONFIG 階段進行的,我們需要掌握 location 的匹配規則和匹配順序。

4.1 location 匹配規則

規則 匹配
= 嚴格匹配。如果請求匹配這個 location,那么將停止搜索并立即處理此請求
~ 區分大小寫匹配(可用正則表達式)
~* 不區分大小寫匹配(可用正則表達式)
!~ 區分大小寫不匹配
!~* 不區分大小寫不匹配
^~ 前綴匹配
@ “@” 定義一個命名的location,使用在內部定向時
/ 通用匹配,任何請求都會匹配到

4.2 location 匹配順序

  • “=” 精準匹配,如果匹配成功,則停止其他匹配
  • 普通字符串指令匹配,優先級是從長到短(匹配字符越多,則選擇該匹配結果)。匹配成功的location如果使用^~,則停止其他匹配(正則匹配)
  • 正則表達式指令匹配,按照配置文件里的順序(從上到下),成功就停止其他匹配
  • 如果正則匹配成功,使用該結果;否則使用普通字符串匹配結果

有一個簡單總結如下:

(location =) > (location 完整路徑) > (location ^~ 路徑) > (location ,* 正則順序) > (location 部分起始路徑) > (location /)

即:

(精確匹配)> (最長字符串匹配,但完全匹配) >(非正則匹配)>(正則匹配)>(最長字符串匹配,不完全匹配)>(location通配)

圖片描述

5. 實驗

5.1 realip 模塊使用

realip 模塊默認沒有被編譯進 Nginx 的,我們需要在源碼編譯階段使用–with-http_realip_module,將 realip 模塊編譯進來后方可使用。接下來,我們做個簡單測試,首先準備一個 server 塊如下:

server {
    listen 8007;
    server_name localhost;
    set_real_ip_from 218.19.206.164;
    real_ip_recursive off;
    # real_ip_recursive on;
    real_ip_header X-Forwarded-For;
    location / {
       return 200 "client real ip: $remote_addr\n";
    }
}

首先,我們將 real_ip_recursive 設置為 off,然后做一次請求:

$ curl -H "X-Forwarded-For: 1.1.1.1,218.19.206.164" http://主機ip:8007 
client real ip: 218.19.206.164  

這里返回的是頭部參數 X-Forwarded-For 中最后一個 ip,如果將 real_ip_recursive 設置為 on,此時,由于 set_real_ip_from 中設置218.19.206.164為信任的方向代理 ip,那么 Nginx 會往前找一位,認為 1.1.1.1 是用戶的真實ip。

$ ./nginx -s reload
$ curl -H "X-Forwarded-For: 1.1.1.1,218.19.206.164" http://主機ip:8007  
client real ip: 1.1.1.1

5.2 return 指令和 if 指令聯合使用

我們寫一個簡單配置如下:

server {
    server_name return_and_if.test.com;
    listen 8008;

    root html;
    # 404錯誤跳轉到403.html頁面,根路徑由root指令指定
    error_page 404 /403.html;

    # return 405 '405 Not Allowed!\n';
    location / {
       if ( $request_method = POST ) {
          return 200 "Post Request!\n";
       }
    }
}

先測試if指令,當請求方法為 POST 時,我們能得到 ‘post request!’ 這樣的字符串輸出。GET 請求時候,針對 404 情況,會跳轉到/403.html,我們準備一個 403.html 頁面,里面寫上’403, forbidden!’ 這一行內容,開始下面的 Http 請求:

$ curl -XPOST http://180.76.152.113:8008  
Post Request!

$ curl http://180.76.152.113:8008/a.txt  
403, forbidden!

如果我們打開 return 405 這行指令,則 error_page 將不會生效,連同后面的 location 匹配也不會生效。無論我們發送如何請求,都會返回405的錯誤信息。這是因為 server 中的 return 指令是在 SERVER_REWRITE中執行的,而 location 匹配則是在下一個階段 FIND_CONFIG 中執行的,所以上一個階段在 return 后,根本不會進入后面的階段執行。

$ curl http://180.76.152.113:8009  
405 Not Allowed!

5.3 rewrite 模塊使用

首先,我們準備環境,首先是新建一個目錄 third(全路徑為/root/test/third),再該目錄下新建一個文件 3.txt, 里面只有一行內容 ‘hello, world’。接下來,我們準備一個 server 塊,加到 Http 指令塊中:

server {
   server_name rewrite.test.com;
   listen 8009;
   # 打開rewrite日志,可以看到對應的rewrite結果
   rewrite_log on;
   error_log logs/rewrite_error.log notice;

   root /root/test/;

   location /first {
      rewrite /first/(.*) /second/$1 last;
      return 200 'first!';
   }

   location /second {
      rewrite /second/(.*) /third/$1 break;
      # rewrite /second/(.*) /third/$1;
      return 200 'second!';
   }  

   location /third {
      return 200 'third!';
   }

}

上述配置中,要打開 rewrite_log指令,這樣我們可以看到 rewrite 指令的相應日志,方便查看結果。

當我們在 /second 配置中,使用 break 時,請求命令:

$ curl http://主機ip:8009/first/3.txt 
hello, world

如果是不使用 break 標識,則請求結果如下:

$ curl http://主機ip:8009/first/3.txt 
second!

首先是 /first/3.txt 請求在 /first 中匹配,并被替換為 /second/3.txt, last 標識表示將繼續進行匹配,在 /second 中,uri 又被 rewrite 成 /third/3.txt, 如果后面跟了 break 標識,表示 rewrite 到此結束,不會執行后面的 return 指令,直接請求靜態資源 /third/3.txt,得到其內容’hello, world’;如果是沒有 break 標識,則會在執行 return 指令后直接返回,并不會繼續執行下去,最后返回’second!'字符串。

5.4 location 匹配

server {
   server_name location.test.com;
   listen 8010;

   location = / {  
      return 200 "精確匹配/";
   }

   location ~* /ma.*ch {
      return 200 "正則匹配/ma.*ch";
   }

   location ~ /mat.*ch {
      return 200 "正則匹配/match.*";
   }


   location = /test {
      return 200 "精確匹配/test";
   }

   location ^~ /test/ {
      return 200 "前綴匹配/test";
   }

   location ~ /test/he*o {
      return 200 "正則匹配/test/he*o";
   }

   location / {  
      return 200 "通配/";
   }

}

我們按照這樣的 location 規則,進行匹配實驗,結果如下:

# 精確匹配優先級最高
$ curl http://localhost:8010/
精確匹配/

$ curl http://localhost:8010/test 
精確匹配/test

# 前綴匹配優先級高于正則匹配
$ curl http://180.76.152.113:8010/test/heeo 
前綴匹配/test

# 正則匹配,按照順序依次匹配,如果同時匹配兩個正則,則前面的優先匹配
$ curl http://180.76.152.113:8010/matxxch 
正則匹配/ma.*ch

# 什么都匹配不到時,最后匹配通配/
$ curl http://180.76.152.113:8010/xxxxx 
通配/

6. 小結

這里介紹了 Nginx 處理 Http 請求的 11 個階段,并重點介紹了 前三個階段POST_READ、REWRITE以及FIND_CONFIG以及這些階段中涉及到的模塊和指令。前面講到的指令都是 Nginx 中的高頻指令,必須要熟練掌握。