前言
清明假期前,我們需要執行一個批量操作,把騰訊雲的視訊下載下傳下來,使用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字串裡面,而
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
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
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);...
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...
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);...
https://blog.csdn.net/ITlanyue/article/details/117786720
一定要為HttpUrlConnection設定connectTimeout屬性以防止連接配接被阻塞 - aLa神燈 - 部落格園需要給連接配接代碼追加一個逾時設定,即通過以下設定代碼追加一個逾時期限: conn.setConnectTimeout(3000); 這時,我們設定為逾時時間為3秒,如果3秒内不能連接配接就被認為是有錯誤發生
https://www.cnblogs.com/lxh520/p/8413665.html