1. 程式人生 > >Nginx 的過濾模組是幹啥用的?

Nginx 的過濾模組是幹啥用的?

上一篇文章我寫了 [Nginx 的 11 個階段](https://iziyang.github.io/2020/04/12/5-nginx/),很多人都說太長了。這是出於文章完整性的考慮的,11 個階段嘛,一次性說完就完事了。今天這篇文章比較短,看完沒問題。 ## 過濾模組的位置 之前我們介紹了 Nginx 的 11 個階段,在 content 階段時,Nginx 會生成返回給使用者的響應內容,對使用者的響應內容,實際上還需要做再加工處理,Nginx 的過濾模組就是對響應內容進行再加工處理的。所以實際上過濾模組位於 content 階段之後,log 階段之前。 我們先來看一段配置指令: ```nginx limit_req zone=req_one burst=120; limit_conn c_zone 1; satisfy any; allow 192.168.1.0/32; auth_basic_user_file access.pass; gzip on; image_filter resize 80 80; ``` 那麼在這一段配置指令之下,會遵循怎樣的請求流程呢?請看一下下面這張圖: ![](https://img2020.cnblogs.com/other/1833725/202005/1833725-20200527083314534-1524763425.jpg) 上面這張圖的流程大致說一下,如果對於 Nginx 的 11 個階段不瞭解的去翻一下之前的文章。 我這裡再簡單說一下。首先由 Nginx 框架接收 HTTP 請求,經過 preaccess、access、content 階段的處理,當經過 static 模組之後生成響應的時候,很多時候需要對響應進行處理,然後才會返回給客戶端。 這裡我們假如響應是一張圖片的話,那麼需要做縮圖的時候,首先就要經過 image_filter 模組的處理。這裡面還有一個 gzip 模組,這兩個模組也是需要遵循嚴格的順序的。因為如果先做 gzip 壓縮的話,縮圖後面就沒辦法做了。 第二個需要關注的地方是,首先對 header 進行過濾,再對 body 進行過濾。因為我們在對使用者傳送響應的時候,一定是先發送 header,然後再發送 body,所以所有的過濾模組都會提供對 header 或 body 的過濾,當然 image_filter 和 gzip 模組對這兩者都可以過濾。 ## 返回響應 前面我們說過,Nginx 的 11 個階段是有嚴格的順序的,而這個順序是在 Nginx 的程式碼中以一個數組的形式存在的,這個陣列的順序是從後往前。在給使用者返回響應的時候,過濾模組也是有嚴格順序的,這個順序同樣是從後往前。來看一下在程式碼中的定義,標紅的是我下面會提到的幾個過濾模組。 ![](https://img2020.cnblogs.com/other/1833725/202005/1833725-20200527083315320-1417299334.png) 我們需要重點關注四個過濾模組,它們分別的作用是啥呢? - copy_filter:複製包體內容 當我們使用 sendfile 指令的時候,也就是零拷貝技術,不經過使用者態記憶體,這裡就是不經過 Nginx 直接發給使用者,同時也用了 gzip 模組的時候,gzip 是必須在 copy_filter 模組之後的,因為 gzip 必須對記憶體中的資料做壓縮,這時 copy_filter 就會讓 sendfile 指令失效。有些模組不需要對記憶體中的資料進行處理,就需要在 copy_filter 模組之前進行處理。 - postpone_filter:處理子請求 用來處理子請求,有些過濾模組需要關心子請求的處理結果,需要放在該模組之後。 - header_filter:構造響應頭部 用來構造最終傳送給使用者的響應頭部,可能會新增一些 Server,Nginx 版本號等內容。 - write_filter:傳送響應 用來實際呼叫作業系統的 write 或 send 等系統呼叫,來把響應實際傳送出去。 介紹完了過濾模組的功能以及所處的階段,下面來具體看兩個模組。 ## sub 模組 介紹一個可以替換響應中字串內容的模組:sub 模組。 - 功能:將響應中指定的字串,替換成新的字串 - 模組:`ngx_http_sub_filter_module` 模組,預設未編譯進 Nginx,通過 --with-http_sub_module 啟用 ### 指令 ```nginx Syntax: sub_filter string replacement; Default: — Context: http, server, location Syntax: sub_filter_last_modified on | off; Default: sub_filter_last_modified off; Context: http, server, location Syntax: sub_filter_once on | off; Default: sub_filter_once on; Context: http, server, location Syntax: sub_filter_types mime-type ...; Default: sub_filter_types text/html; Context: http, server, location ``` 來解釋一下這四個指令都是啥意思。 - `sub_filter string replacement` `sub_filter` 指令會把匹配到的 `string` 字串替換成 `replacement` 表示的字串。 - `sub_filter_last_modified on | off` `sub_filter_last_modified` 指令的意思是,是否要返回原來的 `last_modified` HTTP 頭部,因為我們已經修改了檔案內容,如果是 `on` 的話,就會繼續返回原來的頭部。 - `sub_filter_once on | off` `sub_filter_once` 的意思是,是否只替換一次,預設開啟,如果設定為 `off` 的話,那就會將響應中的內容全部掃描一遍並替換。 - `sub_filter_types mime-type` 這個指令是說針對那些檔案型別進行替換,這裡可以設定成 \*,但是效率就會比較低了,需要根據實際情況考慮。 ### 實戰 配置檔案如下: ```nginx server { server_name sub.ziyang.com; error_log logs/myerror.log info; location / { sub_filter 'Nginx.oRg' '$host/nginx'; sub_filter 'nginX.cOm' '$host/nginx'; #sub_filter_once on; sub_filter_once off; #sub_filter_last_modified off; sub_filter_last_modified on; } } ``` 這裡需要重新編譯 Nginx,我這裡把下一節需要的模組也一起編譯進去了: ```shell ./configure --prefix=/Users/mtdp/myproject/nginx/test_nginx --with-http_sub_module --with-http_addition_module --with-http_realip_module make cp nginx ../../test_nginx/sbin/ # 複製編譯好的 nginx 到之前的目錄 ``` 執行熱部署: > 熱部署的流程詳見 [Nginx 入門及命令列操作](https://iziyang.github.io/2020/03/10/1-nginx/) ```shell kill -USR2 87693 # 使用新的 Nginx 二進位制檔案提供服務 kill -WINCH 87693 # 退出老的 Nginx 的 worker 程序 kill -quit 87693 # 優雅的退出老的 master 程序 ``` 在瀏覽器中開啟 sub.ziyang.com: ![](https://img2020.cnblogs.com/other/1833725/202005/1833725-20200527083315519-90095891.jpg) 這裡面會發現,nginx.org 已經替換成了 sub.ziyang.com/nginx,nginx.com 也替換成了 sub.ziyang.com/nginx。 ## addition 模組 下面再來看一個過濾模組,addition 模組,它可以在響應的前後新增內容。 - 功能:在相應前或者響應後增加內容,增加內容的方式,是通過新增子請求,根據子請求的響應來完成。 - 模組:`ngx_http_addition_filter_module` 預設未編譯進 Nginx,通過 --with-http_addition_module 啟用 ### 指令 ```nginx Syntax: add_before_body uri; Default: — Context: http, server, location Syntax: add_after_body uri; Default: — Context: http, server, location Syntax: addition_types mime-type ...; Default: addition_types text/html; Context: http, server, location ``` 這裡的三個指令都比較簡單,說一下 `add_before_body` 和 `add_after_body` 後面的 `uri`,這個的意思是說,向指定 `uri` 發起子請求,根據子請求的響應來新增內容。 `addition_types` 指令是指定要新增的檔案型別。 ### 實戰 配置檔案如下: ```nginx server { server_name addition.ziyang.com; error_log logs/myerror.log info; location / { #add_before_body /before_action; #add_after_body /after_action; #addition_types *; } location /before_action { return 200 'new content before\n'; } location /after_action { return 200 'new content after\n'; } location /testhost { uninitialized_variable_warn on; set $foo 'testhost'; return 200 '$gzip_ratio\n'; } } ``` 先看在註釋掉 addition 模組的指令的情況下,會是什麼效果: ```shell ➜ ~ curl addition.ziyang.com/a.txt a ``` 然後開啟註釋: ```shell ➜ ~ curl addition.ziyang.com/a.txt new content before a new content after ``` 在原響應前後都增加了內容。 這裡面需要注意一點,在實際情況下,`add_before_body` 和 `add_after_body` 後面是其他的 uri,我這裡為了簡化,直接轉發到對應的 location。 > 本文涉及到的所有配置檔案我已經放在了 [Nginx 配置檔案](https://docs.qq.com/doc/DSkFoT3pvUVhsaWhT),大家可以自取。 --- 本文首發於我的個人部落格:[iziyang.github.io](https://iziyang.gi