背景
后端服務(wù)中有用到OSS的對象存儲服務(wù)校仑,完成文件上傳操作梢灭,其中有這樣一個場景:
問題定位
- 剛開始發(fā)現(xiàn)這個問題以為是線程池不夠用其掂,通過調(diào)整線程池大小缤底,發(fā)現(xiàn)服務(wù)能支持時間長點(diǎn)嗅定,但壓測一段時間發(fā)現(xiàn)還是會卡死自娩,線程被打滿。
- 后端又懷疑是不是使用@Asnyc線程嵌套導(dǎo)致的渠退,去掉改成同步忙迁,問題依然存在
- 然后就排查代碼看是不是那塊資源未釋放(查了好幾遍沒發(fā)現(xiàn)問題,該close的資源都close了)
- 后面有浮現(xiàn)了幾次后發(fā)現(xiàn)碎乃,每次上傳1000文件姊扔,就會有1000個線程
CLOSE_WAIT
就很奇怪,線程死活不關(guān)閉梅誓,然后就針對OSS相關(guān)代碼做排查恰梢,一行一行把oss相關(guān)注釋后佛南,發(fā)現(xiàn)getFileSize()去掉后,再沒有線程 CLOSE_WAIT 情況嵌言,就是這家伙惹的禍嗅回。。摧茴。绵载。定位完畢(而時間已經(jīng)是凌晨2點(diǎn)多了),欲哭無淚呀苛白。OSS還有這個坑娃豹。血的教訓(xùn)。
/**
* 獲取文件大小
*
* @param fileURL 文件的url(標(biāo)準(zhǔn)oss地址)
*/
public Long getFileSize(String fileURL) {
// 解析bucketName
String bucketName = getBucketName(fileURL);
// 解析objectName
String objectName = getObjectName(bucketName, fileURL);
return s3client.getObject(bucketName, bucketName).getObjectMetadata().getInstanceLength();
}
問題就處在 s3client.getObject(bucketName, bucketName).getObjectMetadata().getInstanceLength();
這行代碼丸氛。
oss SDK獲取文件大小培愁,應(yīng)該調(diào)用getMetaData方法,代碼里調(diào)用的getObject().getMetaData缓窜,相當(dāng)于下載文件但是僅獲取http頭定续,OSS服務(wù)側(cè)任務(wù)數(shù)據(jù)傳輸已完畢然后就斷開連接了,本地獲取到了文件流但是沒有讀取禾锤,此時就會導(dǎo)致CLOSE_WAIT私股,對應(yīng)的tcp連接recv-q隊列有值,send-q隊列大小為0恩掷,表示應(yīng)用已獲取了數(shù)據(jù)但是還沒來得及獲取遠(yuǎn)程就關(guān)閉了連接倡鲸,該連接不會再進(jìn)入CLOSED狀態(tài)张抄,非CLOSED狀態(tài)的連接不會被復(fù)用闽坡,連接一直不釋放進(jìn)而引發(fā)連接池打滿的情況
解決方案
/**
* 獲取文件大小
*
* @param fileURL 文件的url(標(biāo)準(zhǔn)oss地址)
*/
public Long getFileSize(String fileURL) {
// 解析bucketName
String bucketName = getBucketName(fileURL);
// 解析objectName
String objectName = getObjectName(bucketName, fileURL);
return s3client.getObjectMetadata(bucketName, objectName).getInstanceLength();
}
感悟
后面再用三方sdk的時候,特別是這種使用到線程池先關(guān)的泊窘,一定要做好壓測逼争,針對用到的每一個方法多看看源碼和底層實(shí)現(xiàn)优床,做好資源回收,做好資源回收誓焦,做好資源回收5ǔā!杂伟!