前言之前看磊君写的wiki:Nginx配置中large_client_header_buffers的问题排查 ,其中提到:
对这个结论,心存疑虑,总觉得这种设计很奇怪,于是自己做了个测试,希望能了解的更深入一些。 测试方法
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 第一次测试结果测试得到的结果和磊君的结果不同,该长url请求成功被nginx处理。 什么情况啊?和磊君的结论完全不同。于是查看和磊君环境上的不同,发现很重要的一点:<span style='color:red;'>我只有这一个vhost。</span> 于是添加了另外一个vhost,添加vhost配置如下:(<span style='color:red;'>没有设置 large_client_header_buffers</span>) server {
listen 80;
server_name db.;
......
} 第二次测试结果测试发现,nginx依旧可以处理该长url请求。 再次思考不同点,想到:这些vhost是被主配置中include进来的,是否会和读取顺序有关呢? 于是再次调整配置,将两个vhost放到了一个conf文件中,配置如下: server {
listen 80;
server_name db.;
......
}
server {
listen 80 ;
server_name www.;
large_client_header_buffers 4 1m;
......
} 第三次测试结果得到和磊君相同的结果,nginx返回 带着好奇心,我颠倒了下两个vhost的顺序,如下: server {
listen 80 ;
server_name www.;
large_client_header_buffers 4 1m;
......
}
server {
listen 80;
server_name db.;
......
} 第四次测试结果nginx成功处理该长url请求。 初步结论通过上面的现象,我得到一个初步结论:<span style='color:blue;'>在第一个vhost中配置的large_client_header_buffers参数会起作用。</span> 好奇怪的现象啊,我对自己得出的结论也是心存疑惑,于是决定从手册中好好读下控制header_buffer相关的指令。 从手册上理解nginx有关header_buffer配置指令从手册上找到有两个指令和header_buffer有关:对nginx处理header时的方法,学习后理解如下:
根据对手册的理解,我理解这两个指令在配置header_buffer时的使用场景是不同的,个人理解如下:
为了印证自己对两个配置指令的理解,我把large_client_header_buffer换成client_header_buffer_size,重新跑上面的多种测试,得到了和之前各种场景相同的结论。 手册上也只是说明了这两个指令的使用场景,没有说更多的东西了,之前的疑惑还是没有得到解答,那么只有最后一招了,也是绝招:<span style='color:red;'>从源码中寻找答案!</span> 源码学习这里从client_header_buffer_size指令入手,先查看这个指令的定义部分: { ngx_string('client_header_buffer_size'),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, //可以定义在http{}或server{}中,需要携带一个参数
ngx_conf_set_size_slot, //参数意义为size,使用nginx预定义的解析size参数方法解析
NGX_HTTP_SRV_CONF_OFFSET, //将参数值放到srv级别的conf中
offsetof(ngx_http_core_srv_conf_t, client_header_buffer_size), //解析后放到ngx_http_core_srv_conf_t结构体的client_header_buffer_size中
NULL }
由定义看到,我们在server{}中解析到的值会和http{}中的值做一次merge,作为该server{}下的最终值。查看merge相关的逻辑: 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; }
从这段逻辑中得到结论:如果我们在server{}中配置了client_header_buffer_size,那么针对这个server{}块的最终值应该就是我们配置的值。 为了印证我的结论,我重新写了vhost配置,并在代码中加入调试信息,把最终结果打印出来: <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;'>调试代码:</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,测试每个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 从值的最终结果看,的确是之前设置的1m,但是请求时却返回了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,但是这次就可以正常处理请求了。<span style='color:blue;'>看来nginx在实际处理请求的过程中,一定还有之前不知道的一套逻辑,用来判断client_header_buffer_size的最终值。</span> nginx处理请求时的相关代码如下: 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中经过merge的最终值。default_server在nginx中的定义为:在listen指令中定义
为了验证这一点,我修改vhost配置为: server {
listen 80;
server_name db.;
......
}
server {
listen 80 default; //指定该server为default_server
server_name www.;
client_header_buffer_size 1m;
......
} 重启nginx观察merge结果: 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结果没有不同。测试请求,这次nginx成功处理该请求,和预期的效果一致。 结束语笔者又测试了large_client_header_buffers,得到和client_header_buffer_size同样的结果。可以得出结论:nginx在处理header时实际分配的buffer大小,是解析conf后,default_server中的最终值。 个人水平有限,上面的测试方法和理解如有不当的地方,还望大家指正,谢谢! |
|
来自: waitingnothing > 《网关》