nginx post 500

5 篇文章 0 订阅

这个问题是其他同事反映过来的,应该说比较罕见,需要同时满足三种条件才能发生。为了保持神秘,原因暂时不提,不过背景得交待一下。该案例的大概架构就是部署两个nginx服务器,nginx1作为普通的web server,nginx2作为反向代理部署在nginx1的后端。出于测试目的,取消了临时文件所在目录client_body_temp的访问权限,此为条件一 。

关于client_body_temp目录的作用,简单说就是如果客户端POST一个比较大的文件,长度超过了nginx缓冲区的大小,需要把这个文件的部分或者全部内容暂存到client_body_temp目录下的临时文件。

引起我们注意的是nginx的一个配置指令,client_body_buffer_size,如果把它设置为比较大的数值,例如256k,那么,无论使用firefox还是IE浏览器,来提交任意小于256k的图片,都很正常。如果注释该指令,使用默认的client_body_buffer_size设置,也就是操作系统页面大小的两倍,8k或者16k(此为条件二 ),问题就出现了。

无论使用firefox4.0还是IE8.0,提交一个比较大,200k左右的图片,都返回500 Internal Server Error错误。这其实也没有问题,200k大于当前的client_body_buffer_size(8k或者16k),提交的内容需要写入临时文件,前面取消了目录访问权限导致出错。

但是,如果提交一个比较小,30k左右的图片,firefox和IE的返回结果却有所不同。IE仍然返回500错误,这很好理解,30k仍然大于当前的client_body_buffer_size(8k或者16k),出错是正常的,不出错才奇怪。然而,firefox就是神奇地返回了经过resize server处理后的页面!

这太奇怪了,难道firefox发送的数据与IE发送的有所不同?使用tcpdump抓包发现,的确是有很大的不一样。

IE发送的数据包截图如下,建立连接时三次握手清晰可见,第10行是IE向nginx1发送http头消息,第24行是nginx1发送应答,然后再发送Content-Disposition和Content-Type两行header和body。Body与header的主干部分是分开不同的包发送的。


而firefox发送的数据包截图如下,三次握手不再赘述,第13行是firefox向nginx1发送http头消息,奇怪的是除了发送头消息,还附带了部分body,见第33行,第35行才是nginx1对该头消息的应答。也就是说,firefox把一部分body塞到header包里。


Firefox的这种行为使同事注意到nginx里设置的另一个缓冲区大小:client_header_buffer_size。原来此前他们设置了该值为128k,此为条件三 。

综合以上现象,就有了初步推断:对于IE的请求,nginx把body只放到body的缓冲区处理,所以不受header的缓冲区大小的影响,而对于firefox的请求,nginx可能会把body放到header的缓冲区处理,所以,分别设置header、body缓冲区为128k、8k/16k的时候,POST 30k的图片能够成功而POST 200k的图片返回500错误,分别设置header、body缓冲区为128k、256k的时候,POST 200k的图片也能成功。

如何验证这个结论呢?究竟什么情况下body数据会放到header缓冲区处理呢?

“有问题,看日志”是一个好习惯。但是默认情况下,nginx记录的日志比较简单,不能满足要求,需要这样打开调试日志功能:

编译nginx,configure时使用--with-debug打开调试信息,然后make && make install
编辑nginx.conf,在server的error_log指令的文件名后面加上debug:
        error_log     logs/8085_error.log debug ;

使用firefox POST 30k左右的图片时,截取到的部分日志如下:
70 2011/05/26 09:53:28 [debug] 23622#0: *33 http client request body preread 2398
71 2011/05/26 09:53:28 [debug] 23622#0: *33 http read client request body
72 2011/05/26 09:53:28 [debug] 23622#0: *33 recv: fd:3 5840 of 28645
73 2011/05/26 09:53:28 [debug] 23622#0: *33 http client request body recv 5840
74 2011/05/26 09:53:28 [debug] 23622#0: *33 http client request body rest 22805

按这几行的关键字,可以搜索到对应的函数为src/http/ngx_http_request_body.c 的ngx_http_read_client_request_body 和ngx_http_do_read_client_request_body。前者会把预读的body(在源代码里,作者把被塞进http头消息包里的那部分body称为preread,即预读的body)暂存到header缓冲区,而只要这块header缓冲区足够大,足以容纳剩下的body的时候,会调用后者把它们也一起读进来。于是,上面30k左右的图片就能穿透过去,看上去不可思议的事情就这样发生了。

简单地分析一下相关源码。

在这里开始正式读取body。IE的请求,preread为零,而Firefox的请求,preread非零:

在168行有个条件判断,rb->rest是未读body的剩余长度,b->end – b->last就是空余的缓冲区大小。当header缓冲区不够大时,显然是不会跑到上面176行那里去的,而是会掉到下面,重新根据client_body_buffer_size的大小分配缓冲区处理。于是,200k那么大的图片就被挡住了。而30k左右的图片,在设置了比较大的client_header_buffer_size的时候是可以过去的。

IE的请求会跑到这里:

前面处理不完的都跑到这里,用了两次client_body_buffer_size,意思是剩余的内容不超过缓冲区大小的1.25倍,一次读完(1.25可能是经验值吧),否则,按缓冲区大小读取。


其实这样的处理流程也是无可厚非的,遇到body比较小,刚好header缓冲区又能够放得下,不用白不用,是不是?

最后,整理一下出现这个问题需要的条件。值得注意的是,目前各种版本的nginx都有这个现象(0.7.68、0.8.54、1.0.2都有) 
1) client_body_temp设置为不可访问,使得没有权限写临时文件
2) client_body_buffer_size使用默认设置,8k或者16k
3) client_header_buffer_size设置得比较大
--------------------- 
作者:yui 
来源:CSDN 
原文:https://blog.csdn.net/yui/article/details/6457922 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值