最近遇到了客戶在從我們的服務器下載文件失敗時的情況。然后把解決方案一並整理一下以備后續。需要說明的是,我們前端都是使用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