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

迁移至Apache HttpClient 4.x后大文件上传遇Connection reset by peer问题

大文件上传失败问题排查(HttpClient 3.x → 4.x迁移)

问题背景

已完成Apache Commons HttpClient 3.x到4.x的迁移,替换了Multithreaded Http Client Connection Manager为PoolingHttpClientManager等核心组件。本地环境(exe安装包/Eclipse运行)大文件上传正常,但部署到云端后,调用接收路径和InputStream的PUT方法上传大文件失败。云端环境中Tomcat WebDAV服务部署在代理服务器后方。

上传实现代码

HttpPut method = null;
try
{
  method = new HttpPut(this.encodePath(path));
  setProxy(method);
}
catch (MalformedURLException | URISyntaxException ex)
{
  throw new HttpURIException();
}

String contentType = getGetContentType();
if ((contentType != null) && (contentType.length() > 0))
{
  method.setHeader("Content-Type", contentType);
}
InputStreamEntity ise = new InputStreamEntity(inputStream);
ise.setChunked(true);
ise.setContentType(contentType);
BufferedHttpEntity bhe = new BufferedHttpEntity(ise);
method.setEntity(bhe);
try
{
  HttpResponse response = this.client.execute(method);
  return (response.getStatusLine().getStatusCode() >= 200) && (response.getStatusLine().getStatusCode() < 300);
}
finally
{
  method.releaseConnection();
}

错误堆栈信息

ERROR - FileUploadHandler     - IO Error while file upload. java.net.SocketException: Connection reset by peer (Write failed) 
at java.net.SocketOutputStream.socketWrite0(Native Method) ~[?:?] 
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110) ~[?:?] 
at java.net.SocketOutputStream.write(SocketOutputStream.java:150) ~[?:?] 
at org.apache.http.impl.io.SessionOutputBufferImpl.streamWrite(SessionOutputBufferImpl.java:124) ~[httpcore-4.4.13.jar:4.4.13] 
at org.apache.http.impl.io.SessionOutputBufferImpl.write(SessionOutputBufferImpl.java:160) ~[httpcore-4.4.13.jar:4.4.13] 
at org.apache.http.impl.io.ContentLengthOutputStream.write(ContentLengthOutputStream.java:113) ~[httpcore-4.4.13.jar:4.4.13] 
at org.apache.http.impl.io.ContentLengthOutputStream.write(ContentLengthOutputStream.java:120) ~[httpcore-4.4.13.jar:4.4.13] 
at org.apache.http.entity.BufferedHttpEntity.writeTo(BufferedHttpEntity.java:105) ~[httpcore-4.4.13.jar:4.4.13] 
at org.apache.http.impl.DefaultBHttpClientConnection.sendRequestEntity(DefaultBHttpClientConnection.java:156) ~[httpcore-4.4.13.jar:4.4.13] 
at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:238) ~[httpcore-4.4.13.jar:4.4.13] 
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:123) ~[httpcore-4.4.13.jar:4.4.13] 
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272) ~[httpclient-4.5.13.jar:4.5.13] 
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186) ~[httpclient-4.5.13.jar:4.5.13] 
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) ~[httpclient-4.5.13.jar:4.5.13] 
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) ~[httpclient-4.5.13.jar:4.5.13] 
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) ~[httpclient-4.5.13.jar:4.5.13] 
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) ~[httpclient-4.5.13.jar:4.5.13] 
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108) ~[httpclient-4.5.13.jar:4.5.13] 
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56) ~[httpclient-4.5.13.jar:4.5.13]

HttpClient配置代码

PoolingHttpClientConnectionManager conManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(40);
connectionManager.setDefaultMaxPerRoute(20);
RequestConfig defaultRequestConfig = RequestConfig.custom().setConnectTimeout(CONNECTION_TIMEOUT).build();

SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(SO_TIMEOUT).build();

HttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(HTTPMETHOD_RETRY_COUNT, false);

HttpClientBuilder clientBuilder = HttpClients.custom().useSystemProperties().setConnectionManager(conManager).setDefaultRequestConfig(defaultRequestConfig).setDefaultSocketConfig(socketConfig).setRetryHandler(retryHandler);

credentials = new UsernamePasswordCredentials(userName, password);
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();

credentialsProvider.setCredentials(getAuthScope(aHttpUrl), credentials);

clientBuilder.setDefaultCredentialsProvider(credentialsProvider);
boolean shouldUseProxy = isProxy(aConfig);

String proxyHost = System.getProperty(PROPERTY_PROXY_HOST);
if (shouldUseProxy)
{
  String proxyPort = System.getProperty(PROPERTY_PROXY_PORT, "80");
  HttpHost proxyHosts = new HttpHost(proxyHost, Integer.parseInt(proxyPort));
  RequestConfig config = RequestConfig.copy(defaultRequestConfig).setProxy(proxyHosts).build();
  clientBuilder.setDefaultRequestConfig(config);
}

解决方案建议

1. 调整超时参数

当前设置的5000ms超时过短,大文件上传需要更长的时间窗口,需补充并调整三类超时:

RequestConfig defaultRequestConfig = RequestConfig.custom()
        .setConnectTimeout(30000) // 连接超时,30秒
        .setConnectionRequestTimeout(30000) // 从连接池获取连接的超时
        .setSocketTimeout(300000) // Socket读写超时,5分钟
        .build();

2. 移除BufferedHttpEntity

大文件场景下,BufferedHttpEntity会将整个文件加载到内存,既浪费资源又可能导致代理因长时间无数据传输断开连接。直接使用InputStreamEntity即可:

// 移除BufferedHttpEntity包装
InputStreamEntity ise = new InputStreamEntity(inputStream);
ise.setChunked(true);
ise.setContentType(contentType);
method.setEntity(ise);

3. 优化分块传输兼容性

部分代理服务器对分块传输(Transfer-Encoding: chunked)支持不佳,若能获取文件长度,建议改为固定长度传输:

// 如果能获取文件长度,替换为FileEntity或设置Content-Length
// 示例:如果inputStream来自File
File file = ...;
FileEntity fe = new FileEntity(file, contentType);
method.setEntity(fe);
// 或者手动设置Content-Length
ise.setContentLength(file.length());
ise.setChunked(false);

4. 调整重试策略

DefaultHttpRequestRetryHandler第二个参数设为false会禁止重试非幂等方法,但PUT是幂等方法,可改为true,确保连接重置类异常能被重试:

HttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(HTTPMETHOD_RETRY_COUNT, true);

5. 检查代理/服务器超时配置

需同步调整云端代理服务器(如Nginx)和Tomcat的超时参数:

  • 代理服务器:调整proxy_connect_timeout、proxy_send_timeout、proxy_read_timeout至5分钟以上
  • Tomcat:修改server.xml中Connector的connectionTimeout、keepAliveTimeout,确保大于客户端超时时间

6. 连接池配置优化

大文件上传占用连接时间较长,可适当调大连接池参数:

connectionManager.setMaxTotal(80);
connectionManager.setDefaultMaxPerRoute(40);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 03:27:06