Nginx的client_header_buffer_size和large_client_header_buffers學習

HULK一線技術雜談發表於2019-04-10

之前看到有人寫的一篇關於nginx配置中large_client_header_buffers的問題排查的文章,其中提到:large_client_header_buffers 雖然也可以在server{}內生效,但是隻有 低於 nginx主配置中的值才有意義。

對這個結論,我心存疑慮,總覺得這種設計很奇怪,於是自己做了個測試,希望能瞭解的更深入一些。

測試方法

nginx主配置中加入配置項:(在主配置中將header大小控制在1k)

Nginx的client_header_buffer_size和large_client_header_buffers學習


刪除所有干擾vhost,僅留下一個:

Nginx的client_header_buffer_size和large_client_header_buffers學習


構造請求的shell:(構造header超過1k的請求)

Nginx的client_header_buffer_size和large_client_header_buffers學習


 第一次測試結果

測試得到的結果和之前看到的文章的結果不同,該長url請求成功被nginx處理。

什麼情況啊?於是檢視和文章中環境上的不同,發現很重要的一點:我只有這一個vhost。

於是新增了另外一個vhost,新增vhost配置如下:(沒有設定 large_client_header_buffers)

Nginx的client_header_buffer_size和large_client_header_buffers學習


 第二次測試結果

測試發現,nginx依舊可以處理該長url請求。

再次思考不同點,想到:這些vhost是被主配置中include進來的,是否會和讀取順序有關呢?

於是再次調整配置,將兩個vhost放到了一個conf檔案中,配置如下:

Nginx的client_header_buffer_size和large_client_header_buffers學習


 第三次測試結果

得到和文章中相同的結果,nginx返回414 Request-URI Too Large。

帶著好奇心,我顛倒了下兩個vhost的順序,如下:

Nginx的client_header_buffer_size和large_client_header_buffers學習


 第四次測試結果

nginx成功處理該長url請求。

初步結論

透過上面的現象,我得到一個初步結論:在第一個vhost中配置的large_client_header_buffers引數會起作用。

好奇怪的現象啊,我對自己得出的結論也是心存疑惑,於是決定從手冊中好好讀下控制header_buffer相關的指令。


從手冊上理解nginx有關header_buffer配置指令

從手冊上找到有兩個指令和header_buffer有關:

1.client_header_buffer_size

2.large_client_header_buffers


對nginx處理header時的方法,學習後理解如下:


1.先處理請求的request_line,之後才是request_header。

2.這兩者的buffer分配策略相同。

3.先根據client_header_buffer_size配置的值分配一個buffer,如果分配的buffer無法容納 request_line/request_header,那麼就會再次根據large_client_header_buffers配置的引數分配large_buffer,如果large_buffer還是無法容納,那麼就會返回414(處理request_line)/400(處理request_header)錯誤。


根據對手冊的理解,我理解這兩個指令在配置header_buffer時的使用場景是不同的,個人理解如下:


1.如果你的請求中的header都很大,那麼應該使用client_header_buffer_size,這樣能減少一次記憶體分配。

2.如果你的請求中只有少量請求header很大,那麼應該使用large_client_header_buffers,因為這樣就僅需在處理大header時才會分配更多的空間,從而減少無謂的記憶體空間浪費。


為了印證自己對兩個配置指令的理解,我把large_client_header_buffer

換成client_header_buffer_size,重新跑上面的多種測試,得到了和之前各種場景相同的結論。


手冊上也只是說明了這兩個指令的使用場景,沒有說更多的東西了,之前的疑惑還是沒有得到解答,那麼只有最後一招了,也是絕招:從原始碼中尋找答案


原始碼學習

這裡從client_header_buffer_size指令入手,先檢視這個指令的定義部分:

Nginx的client_header_buffer_size和large_client_header_buffers學習


由定義看到,我們在server{}中解析到的值會和http{}中的值做一次merge,作為該server{}下的最終值。檢視merge相關的邏輯:

Nginx的client_header_buffer_size和large_client_header_buffers學習

從這段邏輯中得到結論:如果我們在server{}中配置了client_header_buffer_size,那麼針對這個server{}塊的最終值應該就是我們配置的值。


為了印證我的結論,我重新寫了vhost配置,並在程式碼中加入除錯資訊,把最終結果列印出來:

Nginx的client_header_buffer_size和large_client_header_buffers學習


除錯程式碼:

Nginx的client_header_buffer_size和large_client_header_buffers學習


重新編譯nginx,測試每個server{}中client_header_buffer_size的最終值為:

Nginx的client_header_buffer_size和large_client_header_buffers學習

從值的最終結果看,的確是之前設定的1m,但是請求時卻返回了414。

由於將兩個server{}的位置顛倒後可以正常處理請求,所以在顛倒的情況下又測試了下最終值,輸出如下:

Nginx的client_header_buffer_size和large_client_header_buffers學習

最終值的輸出還是1m,但是這次就可以正常處理請求了。


看來nginx在實際處理請求的過程中,一定還有之前不知道的一套邏輯,用來判斷

client_header_buffer_size的最終值。


nginx處理請求時的相關程式碼如下:

Nginx的client_header_buffer_size和large_client_header_buffers學習

這裡真相大白:


原來client_header_buffer_size的最終值,是nginx在解析conf後,default_server中經過merge的最終值。


而default_server在nginx中的定義為:在listen指令中定義:

Nginx的client_header_buffer_size和large_client_header_buffers學習

為了驗證這一點,我修改vhost配置為:

Nginx的client_header_buffer_size和large_client_header_buffers學習

重啟nginx觀察merge結果:

Nginx的client_header_buffer_size和large_client_header_buffers學習

merge結果沒有不同。測試請求,這次nginx成功處理該請求,和預期的效果一致。

結束語

筆者又測試了large_client_header_buffers,得到和client_header_buffer_size同樣的結果。可以得出結論:nginx在處理header時實際分配的buffer大小,是解析conf後,default_server中的最終值。

個人水平有限,上面的測試方法和理解如有不當的地方,還望大家指正,謝謝!

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31555491/viewspace-2640947/,如需轉載,請註明出處,否則將追究法律責任。

相關文章