前言之前看磊君寫的wiki:Nginx配置中l(wèi)arge_client_header_buffers的問題排查 ,其中提到:
對(duì)這個(gè)結(jié)論,心存疑慮,總覺得這種設(shè)計(jì)很奇怪,于是自己做了個(gè)測(cè)試,希望能了解的更深入一些。 測(cè)試方法
http {
include mime.types;
default_type application/octet-stream;
large_client_header_buffers 4 1k;
......
}
server {
listen 80 ;
server_name www.;
large_client_header_buffers 4 1m;
......
}
#!/bin/bash
url='http://www./test.html?debug=1'
for i in {0..1000}
do
var='v$i'
url='${url}&$var=$i'
done
curl $url -x 127.0.0.1:80 -v第一次測(cè)試結(jié)果測(cè)試得到的結(jié)果和磊君的結(jié)果不同,該長(zhǎng)url請(qǐng)求成功被nginx處理。 什么情況???和磊君的結(jié)論完全不同。于是查看和磊君環(huán)境上的不同,發(fā)現(xiàn)很重要的一點(diǎn):<span style='color:red;'>我只有這一個(gè)vhost。</span> 于是添加了另外一個(gè)vhost,添加vhost配置如下:(<span style='color:red;'>沒有設(shè)置 large_client_header_buffers</span>) server {
listen 80;
server_name db.;
......
}第二次測(cè)試結(jié)果測(cè)試發(fā)現(xiàn),nginx依舊可以處理該長(zhǎng)url請(qǐng)求。 再次思考不同點(diǎn),想到:這些vhost是被主配置中include進(jìn)來的,是否會(huì)和讀取順序有關(guān)呢? 于是再次調(diào)整配置,將兩個(gè)vhost放到了一個(gè)conf文件中,配置如下: server {
listen 80;
server_name db.;
......
}
server {
listen 80 ;
server_name www.;
large_client_header_buffers 4 1m;
......
}第三次測(cè)試結(jié)果得到和磊君相同的結(jié)果,nginx返回 帶著好奇心,我顛倒了下兩個(gè)vhost的順序,如下: server {
listen 80 ;
server_name www.;
large_client_header_buffers 4 1m;
......
}
server {
listen 80;
server_name db.;
......
}第四次測(cè)試結(jié)果nginx成功處理該長(zhǎng)url請(qǐng)求。 初步結(jié)論通過上面的現(xiàn)象,我得到一個(gè)初步結(jié)論:<span style='color:blue;'>在第一個(gè)vhost中配置的large_client_header_buffers參數(shù)會(huì)起作用。</span> 好奇怪的現(xiàn)象啊,我對(duì)自己得出的結(jié)論也是心存疑惑,于是決定從手冊(cè)中好好讀下控制header_buffer相關(guān)的指令。 從手冊(cè)上理解nginx有關(guān)header_buffer配置指令從手冊(cè)上找到有兩個(gè)指令和header_buffer有關(guān):對(duì)nginx處理header時(shí)的方法,學(xué)習(xí)后理解如下:
根據(jù)對(duì)手冊(cè)的理解,我理解這兩個(gè)指令在配置header_buffer時(shí)的使用場(chǎng)景是不同的,個(gè)人理解如下:
為了印證自己對(duì)兩個(gè)配置指令的理解,我把large_client_header_buffer換成client_header_buffer_size,重新跑上面的多種測(cè)試,得到了和之前各種場(chǎng)景相同的結(jié)論。 手冊(cè)上也只是說明了這兩個(gè)指令的使用場(chǎng)景,沒有說更多的東西了,之前的疑惑還是沒有得到解答,那么只有最后一招了,也是絕招:<span style='color:red;'>從源碼中尋找答案!</span> 源碼學(xué)習(xí)這里從client_header_buffer_size指令入手,先查看這個(gè)指令的定義部分: { ngx_string('client_header_buffer_size'),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, //可以定義在http{}或server{}中,需要攜帶一個(gè)參數(shù)
ngx_conf_set_size_slot, //參數(shù)意義為size,使用nginx預(yù)定義的解析size參數(shù)方法解析
NGX_HTTP_SRV_CONF_OFFSET, //將參數(shù)值放到srv級(jí)別的conf中
offsetof(ngx_http_core_srv_conf_t, client_header_buffer_size), //解析后放到ngx_http_core_srv_conf_t結(jié)構(gòu)體的client_header_buffer_size中
NULL }
由定義看到,我們?cè)趕erver{}中解析到的值會(huì)和http{}中的值做一次merge,作為該server{}下的最終值。查看merge相關(guān)的邏輯: ngx_conf_merge_size_value(conf->client_header_buffer_size, //conf代表server{},prev代表http{}
prev->client_header_buffer_size, 1024);
#define ngx_conf_merge_size_value(conf, prev, default) if (conf == NGX_CONF_UNSET_SIZE) { conf = (prev == NGX_CONF_UNSET_SIZE) ? default : prev; }
從這段邏輯中得到結(jié)論:如果我們?cè)趕erver{}中配置了client_header_buffer_size,那么針對(duì)這個(gè)server{}塊的最終值應(yīng)該就是我們配置的值。 為了印證我的結(jié)論,我重新寫了vhost配置,并在代碼中加入調(diào)試信息,把最終結(jié)果打印出來: <span style='color:blue;'>vhost配置:</span> http {
include mime.types;
default_type application/octet-stream;
client_header_buffer_size 1k;
......
}
server {
listen 80;
server_name db.;
......
}
server {
listen 80 ;
server_name www.;
client_header_buffer_size 1m;
......
}<span style='color:blue;'>調(diào)試代碼:</span> printf('buffer before merge:\nchild: %lu\nparent: %lu\n\n', conf->client_header_buffer_size, prev->client_header_buffer_size);
......
ngx_conf_merge_size_value(conf->client_header_buffer_size,
prev->client_header_buffer_size, 1024);
......
printf('buffer after merge:\nchild: %lu\nparent: %lu\n\n', conf->client_header_buffer_size, prev->client_header_buffer_size);
重新編譯nginx,測(cè)試每個(gè)server{}中client_header_buffer_size的最終值為: buffer before merge:
child: 18446744073709551615 //由于第一個(gè)server{}中沒有配置,所以這個(gè)是-1(NGX_CONF_UNSET_SIZE)的unsigned long int表示
parent: 1024 //http{}中配置為1k
buffer after merge:
child: 1024
parent: 1024
buffer before merge:
child: 1048576 //第二個(gè)server{}中配置為1m
parent: 1024
buffer after merge:
child: 1048576
parent: 1024從值的最終結(jié)果看,的確是之前設(shè)置的1m,但是請(qǐng)求時(shí)卻返回了414。 buffer before merge:
child: 1048576
parent: 1024
buffer after merge:
child: 1048576
parent: 1024
buffer before merge:
child: 18446744073709551615
parent: 1024
buffer after merge:
child: 1024
parent: 1024最終值的輸出還是1m,但是這次就可以正常處理請(qǐng)求了。<span style='color:blue;'>看來nginx在實(shí)際處理請(qǐng)求的過程中,一定還有之前不知道的一套邏輯,用來判斷client_header_buffer_size的最終值。</span> nginx處理請(qǐng)求時(shí)的相關(guān)代碼如下: ngx_http_core_srv_conf_t *cscf;
......
/* the default server configuration for the address:port */
cscf = addr_conf->default_server;
......
if (c->buffer == NULL) {
c->buffer = ngx_create_temp_buf(c->pool,
cscf->client_header_buffer_size);
<span style='color:blue;'>這里真相大白:</span>原來client_header_buffer_size的最終值,是nginx在解析conf后,default_server中經(jīng)過merge的最終值。default_server在nginx中的定義為:在listen指令中定義
為了驗(yàn)證這一點(diǎn),我修改vhost配置為: server {
listen 80;
server_name db.;
......
}
server {
listen 80 default; //指定該server為default_server
server_name www.;
client_header_buffer_size 1m;
......
}重啟nginx觀察merge結(jié)果: buffer before merge:
child: 18446744073709551615
parent: 1024
buffer after merge:
child: 1024
parent: 1024
buffer before merge:
child: 1048576
parent: 1024
buffer after merge:
child: 1048576
parent: 1024merge結(jié)果沒有不同。測(cè)試請(qǐng)求,這次nginx成功處理該請(qǐng)求,和預(yù)期的效果一致。 結(jié)束語筆者又測(cè)試了large_client_header_buffers,得到和client_header_buffer_size同樣的結(jié)果??梢缘贸鼋Y(jié)論:nginx在處理header時(shí)實(shí)際分配的buffer大小,是解析conf后,default_server中的最終值。 個(gè)人水平有限,上面的測(cè)試方法和理解如有不當(dāng)?shù)牡胤?,還望大家指正,謝謝! |
|
|