首頁 > 軟體

Nginx 504 Gateway Time-out的兩種最新解決方案

2022-08-17 14:03:42

背景:

Nginx做反向代理,springboot為後端服務。

問題:

通過瀏覽器向後臺發起請求夠,由於後臺處理時間長,出現504 Gateway Time-out,實際後臺程式依然在執行。如何解決?

504從哪來:本文的場景下504是nginx返回的。

nginx設定中控制該超時時間的屬性:

Syntax:proxy_read_timeout time;
Default:
proxy_read_timeout 60s;
Context:http, server, location

官方地址:Module ngx_http_proxy_module (nginx.org)

官方描述如下: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.

一個請求有三方參與:瀏覽器,nginx,後臺伺服器。

504的錯誤碼是有nginx返回的。結合官網的解釋,我們可以得出結論:

當nginx與後臺的連結兩次讀取有效資料之間超過設定的時間時,就會產生504超時。nginx會主動關閉與後臺伺服器的連結。注意是兩次成功讀取的間隔,不是整個reponse的時間。

預設情況下proxy_read_timeout時60s。

如果你百度或google,通常解決方式有兩種:提高後臺處理效率增大proxy_read_timeout

增大方法很簡單,proxy_read_timeout  [你期望的時間]。

But,後臺效率提升總是有極限的。而proxy_read_timeout是固定值。總會有些正常業務場景,超過了設定的timeout值。

兩種解決方案

本人解決的問題:上傳excel檔案後,由於檔案大小無法預計,所以後臺處理時間也無法預計。同時還要支援大檔案的上傳。上傳後由後臺解析處理。post請求,返回的是json。

一,關閉read-timout,可以實現,但是生產環境下你敢不設定超時時間麼?所以不建議。

二,既然nginx只要從reponse成功讀取資料兩次的間隔在proxy_read_timeout設定的時間內,就不會超時。那麼我們是不是可以通過持續的向response中寫入資料來保證不超時呢。

答案是肯定的。

想通了這一點,實現就十分簡單。

1,正常上傳檔案。

2,新建一個執行緒。持有response的參照,含有標誌位,滿足條件時迴圈執行,程式開始處理資料前,啟動執行緒。

3,執行緒的功能只有一個,以固定間隔向response中寫入資料。使nginx與後臺連結不超時。

4,這裡就需要注意,我的方法是返回json,同時要持續向response寫入資料,所以我手動拼裝json字串。相當於在之前返回的json中增加一個屬性,名稱隨意,我的叫pending,值隨意,非空即可。我是用英文半形的句號" . "。

5,資料處理完後,回撥執行緒的stop方法,終止執行緒中的迴圈。

注意:如有雷同純屬巧合。如果已經有大佬講過這種解決方式,請艾特我,我立即刪除本文。

保持執行緒程式碼如下:

#上下文程式碼
//獲取鮮橙池executor,具體方式看個人。不會的直接百度,有很多
response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
ResponseKeeper responseKeeper = new ResponseKeeper(response);
executorService.execute(responseKeeper);
#上下文程式碼
 
 
public class ResponseKeeper implements Runnable {
 
        /**
        * 迴圈標誌:true時停止迴圈,終止執行緒
        */
        private boolean done = false;
        
        private HttpServletResponse response;
 
        public void stop(){
            done = true;
        }
 
        public ResponseKeeper(HttpServletResponse response) {
            this.response = response;
        }
 
        @Override
        public void run() {
            try {
                response.getWriter().write("{"pending":"");
                while(!done){
                    response.getWriter().write(".");
                    response.getWriter().flush();
                    LOGGER.error("flush-{}", System.currentTimeMillis());
                    Thread.sleep(1000);
                }
                response.getWriter().write("", "status": "0", "msg":"success"}");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

其他問題:

如果你遇到異常

IllegalStateException – if the getOutputStream method has already been called for this response object

那就說明你的程式中有地方呼叫過了,response.getOutputStream();

只需要與已有程式保持一致使用outputStream即可。

即將response.getWriter() 提換成 response.getOutputStream();

原因簡單來講就是這兩個方法互斥。呼叫了一個就不能呼叫另一個。

總結

到此這篇關於Nginx 504 Gateway Time-out的兩種解決方案的文章就介紹到這了,更多相關Nginx 504 Gateway Time-out內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com