You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用HttpURLConnection跨预签名URL传输文件的并发IO异常解决咨询

预签名URL间文件传输并行调用异常问题

当通过预签名GET URL向预签名PUT URL传输文件时,并行调用API超过3次会触发异常。例如调用10次时,前3次能在1分钟内完成,之后Pod的CPU使用率降至0,15分钟后收到如下错误:

java.io.IOException: Error writing request body to server
at java.base/sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.checkError(HttpURLConnection.java:3807)
at java.base/sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.write(HttpURLConnection.java:3789)
...
at java.base/java.lang.Thread.run(Thread.java:833)
Suppressed: java.io.IOException: insufficient data written
    at java.base/sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.close(HttpURLConnection.java:3834)
    at .....
    ... 3 more

当前使用的传输代码:

public ResponseEntity<String> transferData(Config config) throws IOException {

var inputUrl = config.getInputdUrl();
var outputUrl = config.getOutputdUrl();


var uploadConnection = uploader.getUploadConnection(outputUrl);

try (var inputStream = downloader.getDownloadConnection(inputUrl).getInputStream(); 
     var outputStream = uploadConnection.getOutputStream()) {

    IOUtils.copyLarge(inputStream, outputStream);
    outputStream.flush();
    var resp = uploadConnection.getResponseCode();

    return ResponseEntity
            .status(HttpStatus.OK)
            .body("Transfer done with response:" + resp);
} catch (IOException ioe) {
    return ResponseEntity
            .status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body("An  error occurred while transferring data : " + ioe);
}

控制器中调用方式:

new Thread(() -> transferDataToScality(...)).start();
return ResponseEntity.ok("Downloading data in asynchronous mode successfully started");

解决方案

1. 限制并行线程数,避免资源耗尽

无限制创建线程会耗尽系统资源,导致后续请求无法获取连接、CPU或内存,进而出现写入超时或数据不完整。使用Spring的ThreadPoolTaskExecutor自定义线程池控制并行数:

@Bean
public ThreadPoolTaskExecutor transferTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(3); // 核心线程数,根据Pod资源调整
    executor.setMaxPoolSize(5); // 最大线程数
    executor.setQueueCapacity(10); // 任务队列大小
    executor.setThreadNamePrefix("file-transfer-");
    executor.initialize();
    return executor;
}

调用时改用线程池执行:

transferTaskExecutor.execute(() -> transferDataToScality(...));
return ResponseEntity.ok("Downloading data in asynchronous mode successfully started");

2. 为HTTP连接设置超时时间

未设置超时会导致线程在网络异常时长期阻塞,占用资源无法释放。在获取HttpURLConnection的方法中添加超时配置:

HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setConnectTimeout(30000); // 30秒连接超时
conn.setReadTimeout(60000); // 60秒读取超时
// 其他配置...
return conn;

3. 确保连接正确关闭,释放资源

即使使用try-with-resources,仍需显式关闭HttpURLConnection,避免连接泄漏:

public ResponseEntity<String> transferData(Config config) {
    HttpURLConnection uploadConnection = null;
    HttpURLConnection downloadConnection = null;
    try {
        var inputUrl = config.getInputdUrl();
        var outputUrl = config.getOutputdUrl();

        uploadConnection = uploader.getUploadConnection(outputUrl);
        downloadConnection = downloader.getDownloadConnection(inputUrl);
        
        try (var inputStream = downloadConnection.getInputStream(); 
             var outputStream = uploadConnection.getOutputStream()) {

            IOUtils.copyLarge(inputStream, outputStream);
            outputStream.flush();
            var resp = uploadConnection.getResponseCode();

            return ResponseEntity
                    .status(HttpStatus.OK)
                    .body("Transfer done with response:" + resp);
        }
    } catch (IOException ioe) {
        return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("An error occurred while transferring data : " + ioe);
    } finally {
        if (uploadConnection != null) uploadConnection.disconnect();
        if (downloadConnection != null) downloadConnection.disconnect();
    }
}

4. 检查预签名URL的有效期

预签名URL存在有效期限制,若传输时间超过有效期会被服务端拒绝。确认URL有效期是否覆盖最长传输时间,必要时延长有效期。

5. 调整Pod与JVM资源配置

Pod CPU使用率降至0通常是线程全部阻塞导致无法调度。检查Pod的CPU、内存配额,适当调整资源请求与限制;同时优化JVM参数(如-Xmx、-Xms),避免内存不足引发线程阻塞。

内容的提问来源于stack exchange,提问作者ismala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 13:33:11