天天看點

關于使用URLConnection下載下傳檔案時出現無限等待線程挂起的問題

前言

      清明假期前,我們需要執行一個批量操作,把騰訊雲的視訊下載下傳下來,使用python腳本壓縮并轉碼後上傳到騰訊雲另一個桶。寫完代碼後,跑起來觀察了一會,一切正常。

      然而回來後發現,線程卡住了,而且沒有任何的日志輸出,是以完全不知道問題出在哪裡。重新開機容器後,代碼繼續正常跑,可是跑了幾個小時後,再次出現。

問題排查

        代碼如下:

public boolean downloadNet(String videoUrl, String filePath) throws Exception {
        URL url = new URL(videoUrl);
        URLConnection conn = null;
        try {
            conn = url.openConnection();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

        try {
            InputStream inStream = conn.getInputStream();
            //下載下傳第三方連結視訊到本地
            return FileUtil.videoDownLoadToFile(inStream, filePath);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return false;
    }
           

        首先可以确定,基本邏輯是沒有問題,因為前幾個小時代碼都是可以按照預期正常運作的。問題是随着運作時間變長而出現的,并且控制台沒有輸出任何錯誤資訊。是以我猜想是:

        ①線程死鎖;

        ②資源耗盡,如資源沒有正确釋放,導緻網絡連接配接沒有正确釋放;

        ③其他原因。

        對于原因①,從代碼上看是沒有涉及多線程程式設計和共享資源争搶的,而且通過jstack的分析也沒有發現有死鎖,可以排除。

        對于原因②,由于是在腳本中循環下載下傳,并且沒有顯式釋放連接配接資源,會不會是這個原因呢?然後我進入了 URLConnection 的源碼中,發現裡面沒有聲明 close或者 disconnect 之類的方法,并且有這麼一段注釋:

* Invoking the {@code close()} methods on the {@code InputStream} or {@code OutputStream} of an
* {@code URLConnection} after a request may free network resources associated with this
* instance, unless particular protocol specifications specify different behaviours
* for it.      

        大意是調用URLConnection的輸入流或者輸出流的close方法之後,是可以釋放相關的網絡資源的。而我在下載下傳完檔案到本地後是有關閉對應的輸入流的,是以這也不是問題所在。那麼問題出現在哪裡呢?

        我繼續檢視URLConnection抽象類的源碼,發現了這麼一段描述:

/**
 * Returns setting for connect timeout.
 * <p>
 * 0 return implies that the option is disabled
 * (i.e., timeout of infinity).
 *
 * @return an {@code int} that indicates the connect timeout
 *         value in milliseconds
 * @see #setConnectTimeout(int)
 * @see #connect()
 * @since 1.5
 */
public int getConnectTimeout() {
    return connectTimeout;
}

/**
 * Sets the read timeout to a specified timeout, in
 * milliseconds. A non-zero value specifies the timeout when
 * reading from Input stream when a connection is established to a
 * resource. If the timeout expires before there is data available
 * for read, a java.net.SocketTimeoutException is raised. A
 * timeout of zero is interpreted as an infinite timeout.
 *
 *<p> Some non-standard implementation of this method ignores the
 * specified timeout. To see the read timeout set, please call
 * getReadTimeout().
 *
 * @param timeout an {@code int} that specifies the timeout
 * value to be used in milliseconds
 * @throws IllegalArgumentException if the timeout parameter is negative
 *
 * @see #getReadTimeout()
 * @see InputStream#read()
 * @since 1.5
 */
public void setReadTimeout(int timeout) {
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout can not be negative");
    }
    readTimeout = timeout;
}      

        大意是當connectTimeout和readTimeout不設定值,也就是預設值為0時,連接配接主機逾時和主機讀取資料逾時被設定為無窮時間。

問題解決

        至此,就可以定位到問題所在——程式在某一次連接配接或者讀取資料時網絡發生了異常,導緻一直阻塞。

        解決方法也很簡單,為連接配接增加設定連接配接逾時和讀取資料逾時即可。

public boolean downloadNet(String videoUrl, String filePath) throws Exception {
        URL url = new URL(videoUrl);
        if ("https".equalsIgnoreCase(url.getProtocol())) {
            SslUtils.ignoreSsl();
        }
        URLConnection conn = null;
        try {
            conn = url.openConnection();
            //設定 10 s 連接配接逾時
            conn.setConnectTimeout(10 * 1000);
            //設定 2 min 讀取資料逾時
            conn.setReadTimeout(120 * 1000);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

        try {
            InputStream inStream = conn.getInputStream();
            return FileUtil.videoDownLoadToFile(inStream, filePath);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return false;
    }
           

        為連接配接配置connectTimeout和readTimeout後,腳本資料順利刷完,再也沒有出現阻塞的情況了。

參考連結:

        https://www.cnblogs.com/xiohao/p/8854113.html 

        https://blog.csdn.net/qq_34953641/article/details/62037679?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=1328767.72334.16177105124228189&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control

        https://www.cnblogs.com/xiohao/p/8854113.html

                URLConnection的連接配接、逾時、關閉用法總結_kydkong的部落格-CSDN部落格_urlconnection關閉Java中可以使用HttpURLConnection來請求WEB資源。         1、 URL請求的類别     分為二類,GET與POST請求。二者的差別在于:        a:) get請求可以擷取靜态頁面,也可以把參數放在URL字串後面,傳遞給servlet,        b:) post與get的不同之處在于post的參數不是放在URL字串裡面,而

關于使用URLConnection下載下傳檔案時出現無限等待線程挂起的問題

https://blog.csdn.net/kydkong/article/details/46964055?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-1.no_search_link&spm=1001.2101.3001.4242        https://blog.csdn.net/kydkong/article/details/46964055?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-1.no_search_link&spm=1001.2101.3001.4242

關于使用URLConnection下載下傳檔案時出現無限等待線程挂起的問題

https://blog.csdn.net/kydkong/article/details/46964055?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-1.no_search_link&spm=1001.2101.3001.4242

https://www.iteye.com/blog/frejus-2077975

關于使用URLConnection下載下傳檔案時出現無限等待線程挂起的問題

https://www.iteye.com/blog/frejus-2077975

HttpURLConnection 設定逾時與釋放資源_藍月-CSDN部落格_httpurlconnection 逾時設定1.連接配接時間 等待時間的設定方法(1)全局設定 -- JDK 1.5以前的版本,隻能通過設定這兩個系統屬性來控制網絡逾時。System.setProperty("sun.net.client.defaultConnectTimeout", 逾時毫秒數字元串);System.setProperty("sun.net.client.defaultReadTimeout", 逾時毫秒數字元串); (2局部設定)-- JDK 1.5及之後URL newurl = new URL(url);...

關于使用URLConnection下載下傳檔案時出現無限等待線程挂起的問題

https://lanyue.blog.csdn.net/article/details/117786720?utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-1.no_search_link

使用HttpURLConnection時遇到的資源未釋放的問題_weixin_33924220的部落格-CSDN部落格http://blog.sina.com.cn/s/blog_56beadc60100j9zu.html今天自己寫了一個壓力測試的小程式,同時啟100個線程,每個線程都串行地通路應用伺服器上的一個jsp頁面200次。在程式運作了一會兒以後,問題來了: java.net.SocketException: No buffer space available (maximum connecti...

關于使用URLConnection下載下傳檔案時出現無限等待線程挂起的問題

https://blog.csdn.net/weixin_33924220/article/details/86255959

        HttpURLConnection 設定逾時與釋放資源_藍月-CSDN部落格_httpurlconnection 逾時設定1.連接配接時間 等待時間的設定方法(1)全局設定 -- JDK 1.5以前的版本,隻能通過設定這兩個系統屬性來控制網絡逾時。System.setProperty("sun.net.client.defaultConnectTimeout", 逾時毫秒數字元串);System.setProperty("sun.net.client.defaultReadTimeout", 逾時毫秒數字元串); (2局部設定)-- JDK 1.5及之後URL newurl = new URL(url);...

關于使用URLConnection下載下傳檔案時出現無限等待線程挂起的問題

https://blog.csdn.net/ITlanyue/article/details/117786720

        一定要為HttpUrlConnection設定connectTimeout屬性以防止連接配接被阻塞 - aLa神燈 - 部落格園需要給連接配接代碼追加一個逾時設定,即通過以下設定代碼追加一個逾時期限: conn.setConnectTimeout(3000); 這時,我們設定為逾時時間為3秒,如果3秒内不能連接配接就被認為是有錯誤發生

關于使用URLConnection下載下傳檔案時出現無限等待線程挂起的問題

https://www.cnblogs.com/lxh520/p/8413665.html 

繼續閱讀