Nginx的client_header_buffer_size和large_client_header_buffers學(xué)習(xí)

之前看到有人寫的一篇關(guān)于nginx配置中l(wèi)arge_client_header_buffers的問題排查的文章雀费,其中提到:

large_client_header_buffers 雖然也可以在server{}內(nèi)生效乔询,但是只有 低于 nginx主配置中的值才有意義。

對這個結(jié)論迹冤,我心存疑慮,總覺得這種設(shè)計很奇怪,于是自己做了個測試,希望能了解的更深入一些后室。

測試方法

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

http {
    include  mime.types;
    default_type  application/octet-stream;
    large_client_header_buffers  4 1k;
    ......
}

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

server {
    listen 80;
    server_name  www.job360.com;
    large_client_header_buffers  4 1m;
    ......
}

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

#!/bin/bash

url="http://www.job360.com/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

第一次測試結(jié)果

測試得到的結(jié)果和之前看到的文章的結(jié)果不同混狠,該長url請求成功被nginx處理岸霹。

什么情況啊将饺?于是查看和文章中環(huán)境上的不同贡避,發(fā)現(xiàn)很重要的一點:我只有這一個vhost。

于是添加了另外一個vhost予弧,添加vhost配置如下:(沒有設(shè)置 large_client_header_buffers)

server {
    listen 80;
    server_name db.job360.com;
    ......}

第二次測試結(jié)果

測試發(fā)現(xiàn)刮吧,nginx依舊可以處理該長url請求。

再次思考不同點桌肴,想到:這些vhost是被主配置中include進來的皇筛,是否會和讀取順序有關(guān)呢?

于是再次調(diào)整配置坠七,將兩個vhost放到了一個conf文件中,配置如下:

server {
    listen 80;
    server_name db.job360.com;
    ......
}

server {
    listen 80;
    server_name  www.job360.com;
    large_client_header_buffers  4 1m;
    ......
}

第三次測試結(jié)果

得到和文章中相同的結(jié)果旗笔,nginx返回414 Request-URI Too Large彪置。

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

server {
    listen 80;
    server_name  www.job360.com;
    large_client_header_buffers  4 1m;
    ......
}

server {
    listen 80;
    server_name db.job360.com;
    ......
}

第四次測試結(jié)果

nginx成功處理該長url請求拳魁。

初步結(jié)論

通過上面的現(xiàn)象,我得到一個初步結(jié)論:在第一個vhost中配置的large_client_header_buffers參數(shù)會起作用撮弧。

好奇怪的現(xiàn)象啊潘懊,我對自己得出的結(jié)論也是心存疑惑姚糊,于是決定從手冊中好好讀下控制header_buffer相關(guān)的指令。

從手冊上理解nginx有關(guān)header_buffer配置指令

從手冊上找到有兩個指令和header_buffer有關(guān):

  1. client_header_buffer_size
  2. large_client_header_buffers

對nginx處理header時的方法授舟,學(xué)習(xí)后理解如下:

  1. 先處理請求的request_line救恨,之后才是request_header。
  2. 這兩者的buffer分配策略相同释树。
  3. 先根據(jù)client_header_buffer_size配置的值分配一個buffer肠槽,如果分配的buffer無法容納 request_line/request_header,那么就會再次根據(jù)large_client_header_buffers配置的參數(shù)分配large_buffer奢啥,如果large_buffer還是無法容納秸仙,那么就會返回414(處理request_line)/400(處理request_header)錯誤。

根據(jù)對手冊的理解桩盲,我理解這兩個指令在配置header_buffer時的使用場景是不同的寂纪,個人理解如下:

  1. 如果你的請求中的header都很大,那么應(yīng)該使用client_header_buffer_size赌结,這樣能減少一次內(nèi)存分配捞蛋。
  2. 如果你的請求中只有少量請求header很大,那么應(yīng)該使用large_client_header_buffers姑曙,因為這樣就僅需在處理大header時才會分配更多的空間襟交,從而減少無謂的內(nèi)存空間浪費。

為了印證自己對兩個配置指令的理解伤靠,我把large_client_header_buffer換成client_header_buffer_size捣域,重新跑上面的多種測試,得到了和之前各種場景相同的結(jié)論宴合。

手冊上也只是說明了這兩個指令的使用場景焕梅,沒有說更多的東西了,之前的疑惑還是沒有得到解答卦洽,那么只有最后一招了贞言,也是絕招:從源碼中尋找答案

源碼學(xué)習(xí)

這里從client_header_buffer_size指令入手阀蒂,先查看這個指令的定義部分:

{ ngx_string("client_header_buffer_size"),
  NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,              //可以定義在http{}或server{}中该窗,需要攜帶一個參數(shù)
  ngx_conf_set_size_slot,                                           //參數(shù)意義為size,使用nginx預(yù)定義的解析size參數(shù)方法解析
  NGX_HTTP_SRV_CONF_OFFSET,                                         //將參數(shù)值放到srv級別的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 },

src/http/ngx_http_core_module.c

由定義看到蚤霞,我們在server{}中解析到的值會和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); 

src/http/ngx_http_core_module.c
#define ngx_conf_merge_size_value(conf, prev, default)                       \
    if (conf == NGX_CONF_UNSET_SIZE) {                                       \
        conf = (prev == NGX_CONF_UNSET_SIZE) ? default : prev;               \
    }

src/core/ngx_conf_file.h

從這段邏輯中得到結(jié)論:如果我們在server{}中配置了client_header_buffer_size规肴,那么針對這個server{}塊的最終值應(yīng)該就是我們配置的值。

為了印證我的結(jié)論,我重新寫了vhost配置拖刃,并在代碼中加入調(diào)試信息删壮,把最終結(jié)果打印出來:

http {
    include  mime.types;
    default_type  application/octet-stream;
    large_client_header_buffers  4 1k;
    ......

    server {
        listen 80;
        server_name db.job360.com;
        ......
    }

    server {
        listen 80;
        server_name  www.job360.com;
        large_client_header_buffers  4 1m;
        ......
    }
}

調(diào)試代碼:

    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);

src/http/ngx_http_core_module.c

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

buffer before merge:
child: 18446744073709551615    //由于第一個server{}中沒有配置兑牡,所以這個是-1(NGX_CONF_UNSET_SIZE)的unsigned long int表示
parent: 1024    //http{}中配置為1k

buffer after merge:
child: 1024
parent: 1024

buffer before merge:
child: 1048576    //第二個server{}中配置為1m
parent: 1024

buffer after merge:
child: 1048576
parent: 1024

從值的最終結(jié)果看央碟,的確是之前設(shè)置的1m,但是請求時卻返回了414发绢。

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

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边酒,但是這次就可以正常處理請求了经柴。

看來nginx在實際處理請求的過程中,一定還有之前不知道的一套邏輯墩朦,用來判斷client_header_buffer_size的最終值坯认。

nginx處理請求時的相關(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);

src/http/ngx_http_request.c

這里真相大白:

原來client_header_buffer_size的最終值,是nginx在解析conf后氓涣,default_server中經(jīng)過merge的最終值牛哺。

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

The default_server parameter, if present, will cause the server to become the default server for the specified address:port pair. If none of the directives have the default_server parameter then the first server with the address:port pair will be the default server for this pair.

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

server {
    listen 80;
    server_name db.job360.com;
    ......
}

server {
    listen 80 default;
    server_name  www.job360.com;

    large_client_header_buffers  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: 1024

merge結(jié)果沒有不同劳吠。測試請求引润,這次nginx成功處理該請求,和預(yù)期的效果一致痒玩。

結(jié)束語

筆者又測試了large_client_header_buffers淳附,得到和client_header_buffer_size同樣的結(jié)果〈拦牛可以得出結(jié)論:nginx在處理header時實際分配的buffer大小奴曙,是解析conf后,default_server中的最終值草讶。

個人水平有限洽糟,上面的測試方法和理解如有不當(dāng)?shù)牡胤剑€望大家指正堕战,謝謝坤溃!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嘱丢,隨后出現(xiàn)的幾起案子浇雹,更是在濱河造成了極大的恐慌,老刑警劉巖屿讽,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡伐谈,警方通過查閱死者的電腦和手機烂完,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诵棵,“玉大人抠蚣,你說我怎么就攤上這事÷陌模” “怎么了嘶窄?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長距贷。 經(jīng)常有香客問我柄冲,道長,這世上最難降的妖魔是什么忠蝗? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任现横,我火速辦了婚禮,結(jié)果婚禮上阁最,老公的妹妹穿的比我還像新娘戒祠。我一直安慰自己,他們只是感情好速种,可當(dāng)我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布姜盈。 她就那樣靜靜地躺著,像睡著了一般配阵。 火紅的嫁衣襯著肌膚如雪馏颂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天闸餐,我揣著相機與錄音饱亮,去河邊找鬼。 笑死舍沙,一個胖子當(dāng)著我的面吹牛近上,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拂铡,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼壹无,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了感帅?” 一聲冷哼從身側(cè)響起斗锭,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎失球,沒想到半個月后岖是,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帮毁,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年豺撑,在試婚紗的時候發(fā)現(xiàn)自己被綠了烈疚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡聪轿,死狀恐怖爷肝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情陆错,我是刑警寧澤灯抛,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站音瓷,受9級特大地震影響对嚼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜外莲,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一猪半、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧偷线,春花似錦磨确、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亥曹,卻和暖如春邓了,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背媳瞪。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工骗炉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蛇受。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓句葵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親兢仰。 傳聞我的和親對象是個殘疾皇子乍丈,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,654評論 2 354