代理伺服器和 Web 伺服器通訊中的 504 問題
上週在工作中遇到一個504問題,特寫此文記錄,關注我公眾號的讀者可能閱讀過一篇關於502問題的文章 ofollow,noindex">《502錯誤,讓你進一步明白nginx和php-fpm之間的關係》 。對於一個Web開發者來說,504和502問題看上去好像很簡單,每個人也可能都遇到過,但把問題說清楚並不那麼容易,也希望這兩篇文章能夠幫助您。
兩臺裝置只要通過proxy或fastcgi協議互相通訊,都會遇到504問題,比如Nginx+PHP-FPM會遇到; 代理伺服器連線後端Web服務也會遇到 。我本次遇到的場景屬於後者,重點講解代理導致的504問題。
問題分析
為了把問題說清楚,先介紹下我單位服務部署架構,如下圖:
很多讀者看到https訪問,猜測504問題是不是因它而起,實際上完全沒有關係,但整個部署架構卻因為引入了ssl,導致系統複雜化了。未來ssl肯定是主流,如果你這張圖的部署感興趣,可以看看我的新書 《深入淺出HTTPS:從原理到實戰》 ,裡面描述的很詳細,此處也算作個廣告。
在本文引入這張圖的根本原因是想讓讀者能夠清晰的瞭解我遇到的問題,如果沒有這張圖,讀者在理解的時候會很困難。但也不要想的過於複雜,簡單理解就是nginx作為代理伺服器連線後端的web伺服器(apache/mod_php)。
接下來描述具體遇到問題,在瀏覽器中訪問https://mail.sina.net/x.php的時候,該介面上傳檔案然後儲存到阿里雲OSS上,如果傳輸的檔案非常大,執行時間將會很長,一旦到20秒到時候,必然會出現出現504錯誤,具體如下圖:
順帶說一下,其他頁面和介面沒有遇到該問題,在那一刻會懷疑是不是x.php程式處理有問題(大部分人會這麼理解)。
那到底上面是504錯誤呢,看下wiki的引用:
4 Gateway Timeout The server was acting as a gateway or proxy and did not receive a timely response from the upstream server
它的意思就是一個閘道器或代理伺服器能夠連線後端伺服器,但在讀取伺服器響應的時候超時了 。遇到504問題一般是後端服務的問題,比如:
-
後端程序無故退出了(可能是程式碼異常,也可能是apache或nginx程序異常),導致代理伺服器接收不到後端響應。
-
後端響應緩慢,導致代理伺服器接收後端響應超時了。
解決問題
按照上述可能的兩個情況,逐一分析。
(1)x.php程式在特定的情況下,確實執行緩慢,但apache的access log在25秒左右的時候成功記錄了200訪問日誌(由於php程式碼執行結束後才記錄日誌,一開始可能看不到access日誌,導致開始誤認為是後端程式的問題)。
(2)在x.php程式中記錄應用日誌,應用日誌和access log日誌一樣,沒有任何異常。
這說明程式碼並沒有問題(但程式執行時間過長,有優化的空間),雖然在20秒產生504錯誤(由nginx處理),後端程式碼程序仍然繼續執行,並在25秒成功執行。
排除這個問題後,最有可能是代理伺服器覺得後端響應過於緩慢,主動關閉了該連線,是不是代理伺服器設定的超時時間過短?由於公司的代理伺服器(ssl nginx)是由專人維護的,我看不到具體的配置,郵件詢問了同事,得到回覆如下:
proxy_read_timeout 60 proxy_send_timeout 60
首先看下 proxy_read_timeout 的官方介紹:
Defines a timeout for reading a response from the proxied server. The timeout is set only between two successive read operations, not for the transmission of the whole response. If the proxied server does not transmit anything within this time, the connection is closed.
介紹的很詳細了,得到這個答覆我就很疑惑了,超時時間是60秒,但504在20秒的時候就產生了,大大的問號懸我腦袋上,又仔細看了下官方文件,是不是 proxy_read_timeout 引數的值寫的不嚴謹,官方寫的是60s,可即使寫錯了,nginx 預設的超時時間也是60秒;是不是nginx 版本預設超時時間不一致?官方文件也並沒有對該指令有特殊的說明。
最後同事將該值修改為:
proxy_read_timeout 300 proxy_send_timeout 300
問題最終解決了,肯定是proxy讀取超時了,但具體的配置仍然讓我疑惑。
進一步測試
由於我看不到公司代理伺服器的具體配置,所以我安裝了一個代理伺服器,感興趣的同學也可以進一步瞭解nginx的proxy配置,如果沒有特殊的需求,配置非常簡單。
server { listen443 ssl; server_namewww.simplehttps.com; location / { access_log access.logmain; error_logerror.log; proxy_pass http://127.0.0.1:8080; proxy_read_timeout 5; } }
proxy_pass 可以是一個host、內部域名、ip地址,不用是一個對外的域名。
如果遇到超時問題,觀察error.log日誌,會看到以下錯誤:
2018/09/19 21:01:19 [error] 17034#0: *253 upstream timed out (110: Connection timed out) while reading response header from upstream, client: *.*.*.*, server: www.simplehttps.com, request: "GET /x.php HTTP/1.1", upstream: "http://*.*.*.*:8080/x.php", host: "www.simplehttps.com"
最後我不斷調整nginx的proxy_read_timeout指令和後端x.php程式的執行時間,也沒有遇到工作中遇到的問題(20秒和60秒之間不對稱的問題),只能後續繼續留意了。
總結
1:閘道器和後端的超時時間(proxy或fastcgi)必須協調一致,在本案例中,apache/mod_php執行最長時間如果是30秒,那麼nginx設定的超時時間必須大於30秒,因為必須考慮網路傳輸延時時間(非網路包總傳輸時間);而如果是nginx+php-fpm模式,nginx設定的超時時間相對簡單,因為大部分情況下,nginx和php-fpm部署在同一臺機器上,網路延時相對較短,但必須考慮php最大執行時間和php-fpm最大執行時間,後續我會寫一篇關於php-fpm和php之間協調工作的文章。
2:針對5**錯誤,可認為都是http錯誤碼,都是伺服器端的錯誤(相對於客戶端),在遇到相關錯誤的時候,我們必須根據錯誤碼判斷可能存在的問題,然後再針對性的排查,否則排查時間會增加很多。
如果讀者購買過我的新書《深入淺出HTTPS:從原理到實戰》,可以去豆瓣分享下對本書的一些看法,直接點選文末的
「閱讀原文」即可點評。也可以關注我的公眾號(ID:yudadanwx)和我聊一聊。