之前看到有人写的一篇关于 nginx 配置中 large_client_header_buffers 的问题排查的文章,其中提到:large_client_header_buffers 虽然也可以在 server{}内生效,但是只有 低于 nginx 主配置中的值才有意义。
对这个结论,我心存疑虑,总觉得这种设计很奇怪,于是自己做了个测试,希望能了解的更深入一些。
测试方法
nginx 主配置中加入配置项:(在主配置中将 header 大小控制在 1k)
删除所有干扰 vhost,仅留下一个:
构造请求的 shell:(构造 header 超过 1k 的请求)
1 第一次测试结果
测试得到的结果和之前看到的文章的结果不同,该长 url 请求成功被 nginx 处理。
什么情况啊?于是查看和文章中环境上的不同,发现很重要的一点:我只有这一个 vhost。
于是添加了另外一个 vhost,添加 vhost 配置如下:(没有设置 large_client_header_buffers)
2 第二次测试结果
测试发现,nginx 依旧可以处理该长 url 请求。
再次思考不同点,想到:这些 vhost 是被主配置中 include 进来的,是否会和读取顺序有关呢?
于是再次调整配置,将两个 vhost 放到了一个 conf 文件中,配置如下:
3 第三次测试结果
得到和文章中相同的结果,nginx 返回 414 Request-URI Too Large。
带着好奇心,我颠倒了下两个 vhost 的顺序,如下:
4 第四次测试结果
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 指令入手,先查看这个指令的定义部分:
由定义看到,我们在 server{}中解析到的值会和 http{}中的值做一次 merge,作为该 server{}下的最终值。查看 merge 相关的逻辑:
从这段逻辑中得到结论:如果我们在 server{}中配置了 client_header_buffer_size,那么针对这个 server{}块的最终值应该就是我们配置的值。
为了印证我的结论,我重新写了 vhost 配置,并在代码中加入调试信息,把最终结果打印出来:
调试代码:
重新编译 nginx,测试每个 server{}中 client_header_buffer_size 的最终值为:
从值的最终结果看,的确是之前设置的 1m,但是请求时却返回了 414。
由于将两个 server{}的位置颠倒后可以正常处理请求,所以在颠倒的情况下又测试了下最终值,输出如下:
最终值的输出还是 1m,但是这次就可以正常处理请求了。
看来 nginx 在实际处理请求的过程中,一定还有之前不知道的一套逻辑,用来判断
client_header_buffer_size 的最终值。
nginx 处理请求时的相关代码如下:
这里真相大白:
原来 client_header_buffer_size 的最终值,是 nginx 在解析 conf 后,default_server 中经过 merge 的最终值。
而 default_server 在 nginx 中的定义为:在 listen 指令中定义:
为了验证这一点,我修改 vhost 配置为:
重启 nginx 观察 merge 结果:
merge 结果没有不同。测试请求,这次 nginx 成功处理该请求,和预期的效果一致。
结束语
笔者又测试了 large_client_header_buffers,得到和 client_header_buffer_size 同样的结果。可以得出结论:nginx 在处理 header 时实际分配的 buffer 大小,是解析 conf 后,default_server 中的最终值。
个人水平有限,上面的测试方法和理解如有不当的地方,还望大家指正,谢谢!
本文转载自公众号 360 云计算(ID:hulktalk)。
原文链接:
https://mp.weixin.qq.com/s/cA6vxQYQEIsdAtEgXgGCYw
评论