nginx反向代理下载文件失败处理

最近遇到了客户在从我们的服务器下载文件失败时的情况。然后把解决方案一并整理一下以备后续。需要说明的是,我们前端都是使用nginx来做反向代理,后面的逻辑处理采用php的方式。

1、缓存目录不可写

nginx在做反向代理,代理后端的nginx+php-fpm时,在客户端下载文件时,总是下载到一定的大小,就下载不动,查看后端服务器,表现正常,在绕过前端反向代理,直接从后端下载,也正常。检查反向代理服务器日志,抛出如下异常:

复制代码
2017/01/16 11:54:38 [warn] 3748#0: *61038342 an upstream response is buffered to a temporary file /usr/local/tengine/proxy_temp/5/03/0000000035 while reading upstream, client: 192.168.42.190, server: preview-qa.fdccloud.com, request: "GET /zip/033ca15a28b33298b46bc1e7eb2ea0f24cde16f9/%E9%99%84%E4%BB%B6%E9%97%AE%E9%A2%98/04%E6%98%8E%E6%BA%90%E7%A7%BB%E5%8A%A8V3.0%E4%BA%A7%E5%93%81%E5%8F%91%E5%B8%83%E5%85%AC%E5%91%8A-%E7%A7%BB%E5%8A%A8%E8%AE%A1%E5%88%92.pptx HTTP/1.1", upstream: "http://127.0.0.1:2016/zip/033ca15a28b33298b46bc1e7eb2ea0f24cde16f9/%E9%99%84%E4%BB%B6%E9%97%AE%E9%A2%98/04%E6%98%8E%E6%BA%90%E7%A7%BB%E5%8A%A8V3.0%E4%BA%A7%E5%93%81%E5%8F%91%E5%B8%83%E5%85%AC%E5%91%8A-%E7%A7%BB%E5%8A%A8%E8%AE%A1%E5%88%92.pptx", host: "preview-qa.fdccloud.com"
复制代码
是因为/usr/local/tengine/proxy_temp目录没有权限,修改文件权限为nginx进程用户以后,即正常。
原因分析:
nginx代理nginx时,前端用户请求下载文件, nginx代理会先从后端nginx拿到文件并缓存到本地,然后响应给客户端,其中与proxy buffer相关的配置项如下:
proxy_buffer_size 512k;
proxy_buffers 4 512k;
proxy_busy_buffers_size 512k;
proxy_temp_file_write_size 512k;
由此可知, buffer缓冲区最大可以缓冲2.5M的数据,然后就开始刷写磁盘,如果磁盘无法写入,数据丢失。这也是为什么前端下载部分数据,即下载不动的原因。
 
2、下载大文件超时
 
客户端在下载大文件时,下载到1G时就会显示“下载失败”,FireFox中如果继续下载,则还会再下载1G,然后再失败。反向代理的错误日志如下:
2016/11/25 11:23:47 [error] 67663#0: *11 upstream prematurely closed connection while reading upstream, client: ...

被代理服务器的错误日志:

2016/11/24 23:33:02 [error] 5833#101125: *8559 upstream timed out (60: Operation timed out) while reading response header from upstream, client: ....
原因分析:
 1)、代理服务器报告:上游过早的关闭连接,好像问题出在被代理服务器;而被代理服务器则抱怨:上游服务器超时。那么一个很合理的推论是:代理服务器很长时间没向被代理服务器请求数据,被代理服务器认为代理服务器已经掉线或完成任务,于是主动断开连接,代理服务器发现需要数据,再连接时,已经连接不上了。
 2)、正常的流程应该是:只要客户端一直下载,“客户机->代理服务器->被代理服务器”,这一连串的数据流不会中断,也就不会出现超时。
 3)、出现超时只能有一种情况:代理服务器缓存了大文件。
 4)、代理服务器接到下载请求,向被代理服务器请求数据,由于两个服务器之间网速快,所以代理服务器请求速度要远大于向客户端发送的速度,这就导致一下正常的代理方式:代理服务器要缓存数据。
 5)、但是两个服务器之间的速度实在是太快了,缓存1G数据也就是分分钟的事情,而客户端需要慢慢下载,可能需要十几、甚至几十分钟。代理服务器和被代理服务器这段时间内没有什么事可干,与是两端静默的时间一长,超过了timeout的时间(一般是60s),被代理服务器就认为代理服务器掉线。
 
解决方案:
1)将代理服务器缓存设置更大,可以直接缓存整个文件,跟上面的解决一样
2)禁用代理服务器缓存:
proxy_pass http://192.168.0.1;
proxy_redirect default;
proxy_buffering off;
3)设置更长的超时时间
proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时)
proxy_read_timeout 90; #连接成功后,后端服务器响应时间(代理接收超时)
 
 
小伙伴今天反馈了一个问题,说,网页上传了一个2MB的文件,在网页下载时,只有64KB,并且打开失败。确认该BUG确实存在且必现后,我,踏上了调试解决此BUG之路。

1、系统是nginx+php+mysql,凭经验判断与mysql无关,可以无视TA。 
2、从PHP网页上传2MB文件后,直接在服务器打开该文件,可以正常查阅,并且与原文件二进制一样。 
3、用不同浏览器,不同电脑反复从PHP网页下载该文件,发现下载的文件均只有64KB。 
4、换一个体积只有90KB的文件,从PHP网页上传下载,均无异常。 
通过以上4点,基本可以判定,问题出在nginx上。这时候,打开nginx的日志文件,发现如下错误log, 
[crit] 21636#0: *843968 open() “/home/www/local/nginx/fastcgi_temp/0/11/0000000110” failed (13: Permission denied) while reading upstream,….. 
可以大胆猜测,由于没有足够权限操作fastcgi_temp文件夹,所以无法得到正常的文件,于是,为该文件夹赋上权限后,问题解决。

回头看,这到底是什么原因呢?

查看nginx配置文件,可以找到下面这一段: 
fastcgi_connect_timeout 300; 
fastcgi_send_timeout 300; 
fastcgi_read_timeout 300; 
**fastcgi_buffer_size 64k; 
fastcgi_buffers 4 64k;** 
fastcgi_busy_buffers_size 128k; 
fastcgi_temp_file_write_size 128k; 
每次下载失败时文件的大小总是64KB,应该跟这里有关。原来,nginx会使用fastcgi_buffer_size指定的大小的缓冲区用于缓存fastcgi流的内容。当大小超出此大小时会继续用fastcgi_buffers指定的数量和大小申请缓冲区。如果依然超出此大小,会将多出的内容写入临时文件。也就是说,在本情况下,nginx首先会使用一个64K的缓冲区缓冲fastcgi流的第一部分,超出后最多申请4*64K=256K的缓冲区用于缓冲。如果继续超出,则写入临时文件。所以,在下载大于256K文件的时候,需要用到临时文件夹进行缓冲,而这里没有权限操作,就导致了该问题。
分类: Nginx
原文地址:https://www.cnblogs.com/yaowen/p/9065685.html