nginx+fastcgi文件下载不全的问题

百般思念 提交于 2019-12-06 09:52:22

今天遇到了一个奇怪的问题:nginx+fastcgi+php+某论坛程序的环境下,通过论坛上传的比较大(500KB)的文件下载后体积变小了。而较小的文件(100KB)则安然无恙。

用WinHex比较,可以看到文件从中间被截断了,并且没有多出任何内容。使用经多次测试,每次下载的大小在64KB左右(小于64KB),但不相同。下载到64KB左右的时候卡住了,几秒钟之后下载进度直接跳到100%,显示下载完成。为排除伟大的墙的因素,翻 墙测试,每次下载的大小在127KB左右。

找到论坛源代码,可以看到使用的是php的readfile()进行文件输出,而在输出前已经写入了content-length header。通过HttpFox进行观察,content-length的大小是正确的文件大小。

由于64KB这个大小比较敏感,所以去代码里找了有没有循环缓存读取文件的地方,并没有发现。readfile()直接读取全部内容进行输出。

为了排查,先将服务器切换到httpd(mod_php),则恢复正常,也就是说问题很可能在nginx和fastcgi上。

检查nginx.conf,注意到这个地方:

fastcgi_buffer_size 64k; fastcgi_buffers 4 64k;

它的值正好等于64K,也就是说问题可能和它有关。将其修改为16K,重启nginx服务,重新下载,这次文件的大小变成了16KB左右。

检查日志发现这么一行:

2010/07/15 02:11:40 [crit] 6064#0: *112 open() "/var/lib/nginx/tmp/fastcgi/1/00/0000000001" failed (13: Permission denied)

nginx会使用fastcgi_buffer_size指定的大小的缓冲区用于缓存fastcgi流的内容。当大小超出此大小时会继续用fastcgi_buffers指定的数量和大小申请缓冲区。如果依然超出此大小,会将多出的内容写入临时文件。

也就是说,在本情况下,nginx首先会使用一个64K的缓冲区缓冲fastcgi流的第一部分,超出后最多申请4*64K=256K的缓冲区用于缓冲。如果继续超出,则写入临时文件。

下载100KB的文件时,内存完全足够,不用写入临时文件,所以没有问题。

下载500KB的文件,64KB+64KB*4已经装不下这个文件,需要使用临时文件。

而日志中反应的就是nginx无权写入临时文件。我不清楚nginx在这里具体是如何实现的,个人猜测可能在写入失败后就放弃了后面4个缓冲区,发送完第一个缓冲区后就停止发送。如果是这样,一旦超过keep-alive的时间,服务器会断开tcp连接,浏览器认为文件下载完成。考虑header的大小,浏览器下载的文件略小于64KB。这样一来就能够解释通观察到的现象。

将目录权限问题修正后,问题解决。以后有空可以看看nginx源代码确认一下。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!