Nginx的client_header_buffer_size和large_client_header_buffers学习

2019 年 11 月 19 日

Nginx的client_header_buffer_size和large_client_header_buffers学习

之前看到有人写的一篇关于 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


2019 年 11 月 19 日 23:40293

评论

发布
暂无评论
发现更多内容

平均负载是什么?

我是程序员小贱

Bash 脚本的单元测试

柴锋

bash Linux DevOps Unit Test Shell

全面了解CGI、FastCGI、PHP-FPM

书旅

CGI PHP-FPM Fast-CGI

螺旋矩阵算法,臭代码解析,微服务架构 Service Mesh 服务网格 RPC 协议实现原理 Dubbo 通讯协议,John 易筋 ARTS 打卡 Week 13

John(易筋)

ARTS 打卡计划

MySQL 基准测试

多选参数

MySQL

解析 hashMap 源码之基本操作 get

shengjk1

Java hashmap

解析 HashMap 源码概括

shengjk1

Java hashmap

航运区块链 抗疫危中有机

CECBC区块链专委会

区块链 航运

让你起飞的20个Linux命令骚操作

我是程序员小贱

一次由默认参数引起的思考

Lart

编程 思考

16张图入门Nginx——(前端够用,运维入门)

执鸢者

nginx 运维 前端

1 学习性能优化的要点

我是程序员小贱

解析 HashMap 源码之基本操作 put

shengjk1

Java hashmap

区块链技术--公证人机制

CECBC区块链专委会

区块链 数字货币 公证人

结算场景下的跳坑记

墨凡

正则表达式位置匹配——匹配两个特殊符号中间的内容

jerry.mei

Java 正则表达式 前端 字符串匹配

JDK中居然也有反模式接口常量

看山

Java 源码阅读

spark学习之IDEA配置spark并wordcount提交集群

我是程序员小贱

学习技术先从学会使用搜索引擎开始

我是程序员小贱

Docker搭建PHP+Nginx+MySQL+Redis

书旅

Docker 镜像 lnmp

高效程序员的45个习惯:敏捷开发修炼之道(1)

石云升

读书笔记 敏捷开发

这样看mybatis,谁都会分析源码!

诸葛小猿

源码 mybatis mybatis源码

1 时间复杂度总结

我是程序员小贱

翻译: Effective Go (7)

申屠鹏会

golang 翻译

Rust特征与泛型区别点

编号94530

rust 泛型 封装、继承、多态

目前数字人民币试点仍是“4+1” 别误读了

CECBC区块链专委会

数字货币 央行 人民币

这些年看过的Linux相关书籍推荐

我是程序员小贱

Spring如何选择类构造器

申屠鹏会

golang 翻译

敏捷到底是个什么鬼?

刘华Kenneth

程序员 敏捷 change

毕玄大佬的分享以及给我的感悟

白色蜗牛

Java 程序员 技术 职场 架构师

华为的“少年天才”攀登者,出发向智能存储的“奥林帕斯山”

脑极体

Nginx的client_header_buffer_size和large_client_header_buffers学习-InfoQ