今天遇到了一個奇怪的問題:nginx+fastcgi+php+某論壇程序的環境下,經過論壇上傳的比較大(500KB)的文件下載後體積變小了。而較小的文件(100KB)則安然無恙。php
用WinHex比較,能夠看到文件從中間被截斷了,而且沒有多出任何內容。使用經屢次測試,每次下載的大小在64KB左右(小於64KB),但不相同。下載到64KB左右的時候卡住了,幾秒鐘以後下載進度直接跳到100%,顯示下載完成。爲排除偉大的牆的因素,翻 牆測試,每次下載的大小在127KB左右。nginx
找到論壇源代碼,能夠看到使用的是php的readfile()進行文件輸出,而在輸出前已經寫入了content-length header。經過HttpFox進行觀察,content-length的大小是正確的文件大小。瀏覽器
因爲64KB這個大小比較敏感,因此去代碼裏找了有沒有循環緩存讀取文件的地方,並無發現。readfile()直接讀取所有內容進行輸出。緩存
爲了排查,先將服務器切換到httpd(mod_php),則恢復正常,也就是說問題極可能在nginx和fastcgi上。服務器
檢查nginx.conf,注意到這個地方:tcp
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)it
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源代碼確認一下。