背景
后端服务中有用到OSS的对象存储服务,完成文件上传操作,其中有这样一个场景:
问题定位
- 刚开始发现这个问题以为是线程池不够用,通过调整线程池大小,发现服务能支持时间长点,但压测一段时间发现还是会卡死,线程被打满。
- 后端又怀疑是不是使用@Asnyc线程嵌套导致的,去掉改成同步,问题依然存在
- 然后就排查代码看是不是那块资源未释放(查了好几遍没发现问题,该close的资源都close了)
- 后面有浮现了几次后发现,每次上传1000文件,就会有1000个线程
CLOSE_WAIT
就很奇怪,线程死活不关闭,然后就针对OSS相关代码做排查,一行一行把oss相关注释后,发现getFileSize()去掉后,再没有线程 CLOSE_WAIT 情况,就是这家伙惹的祸。。。。定位完毕(而时间已经是凌晨2点多了),欲哭无泪呀。OSS还有这个坑。血的教训。
/**
* 获取文件大小
*
* @param fileURL 文件的url(标准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获取文件大小,应该调用getMetaData方法,代码里调用的getObject().getMetaData,相当于下载文件但是仅获取http头,OSS服务侧任务数据传输已完毕然后就断开连接了,本地获取到了文件流但是没有读取,此时就会导致CLOSE_WAIT,对应的tcp连接recv-q队列有值,send-q队列大小为0,表示应用已获取了数据但是还没来得及获取远程就关闭了连接,该连接不会再进入CLOSED状态,非CLOSED状态的连接不会被复用,连接一直不释放进而引发连接池打满的情况
解决方案
代码语言:javascript复制/**
* 获取文件大小
*
* @param fileURL 文件的url(标准oss地址)
*/
public Long getFileSize(String fileURL) {
// 解析bucketName
String bucketName = getBucketName(fileURL);
// 解析objectName
String objectName = getObjectName(bucketName, fileURL);
return s3client.getObjectMetadata(bucketName, objectName).getInstanceLength();
}
感悟
后面再用三方sdk的时候,特别是这种使用到线程池先关的,一定要做好压测,针对用到的每一个方法多看看源码和底层实现,做好资源回收,做好资源回收,做好资源回收!!!