迁移至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
相关产品推荐
相关产品推荐

