Java 嵌入式 HttpServer:套接字泄漏
Java embedded HttpServer: socket leak
我有下一个代理处理程序(使用嵌入式 HttpServer 和 Jersey 客户端):
@Override
public void handle(HttpExchange httpExchange) throws IOException {
....
....
WebTarget webTarget = httpClientHolder.getClient().target(url);
try (Response response = webTarget.request().get()) {
if (response.getStatusInfo().getStatusCode() != HttpStatus.SC_OK) {
logger.warn("Failed to download resource, url = " + url + " code = " +
response.getStatusInfo().getStatusCode());
throw new HttpException("Not found", 404);
}
httpExchange.sendResponseHeaders(200, response.getLength());
final InputStream is = response.readEntity(InputStream.class);
try (final OutputStream os = httpExchange.getResponseBody()) {
IOUtils.copy(is, os, 128);
} catch (IOException e) {
logger.warn("Error proxy data", e);
}
}
....
}
有时会发生以下情况
Error proxy data
java.io.IOException: Broken pipe
at sun.nio.ch.FileDispatcherImpl.write0(Native Method) ~[?:1.8.0_191]
at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:47) ~[?:1.8.0_191]
at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93) ~[?:1.8.0_191]
at sun.nio.ch.IOUtil.write(IOUtil.java:65) ~[?:1.8.0_191]
at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471) ~[?:1.8.0_191]
at sun.net.httpserver.Request$WriteStream.write(Request.java:391) ~[?:1.8.0_191]
at sun.net.httpserver.FixedLengthOutputStream.write(FixedLengthOutputStream.java:78) ~[?:1.8.0_191]
at sun.net.httpserver.PlaceholderOutputStream.write(ExchangeImpl.java:444) ~[?:1.8.0_191]
at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:2315) ~[cache-1.0-SNAPSHOT-jar-with-dependencies.jar:?]
at org.apache.commons.io.IOUtils.copy(IOUtils.java:2270) ~[cache-1.0-SNAPSHOT-jar-with-dependencies.jar:?]
at server.TimelineVttHandler.handle(TimelineVttHandler.java:129) [cache-1.0-SNAPSHOT-jar-with-dependencies.jar:?]
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79) [?:1.8.0_191]
at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83) [?:1.8.0_191]
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:82) [?:1.8.0_191]
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:675) [?:1.8.0_191]
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79) [?:1.8.0_191]
at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:647) [?:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_191]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_191]
Suppressed: java.io.IOException: insufficient bytes written to stream
at sun.net.httpserver.FixedLengthOutputStream.close(FixedLengthOutputStream.java:89) ~[?:1.8.0_191]
at sun.net.httpserver.PlaceholderOutputStream.close(ExchangeImpl.java:454) ~[?:1.8.0_191]
at server.TimelineVttHandler.handle(TimelineVttHandler.java:130) [cache-1.0-SNAPSHOT-jar-with-dependencies.jar:?]
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79) [?:1.8.0_191]
at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83) [?:1.8.0_191]
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:82) [?:1.8.0_191]
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:675) [?:1.8.0_191]
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79) [?:1.8.0_191]
at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:647) [?:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_191]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_191]
这对客户端来说不是什么大问题,但资源仍然存在,我们可以通过 lsof 查看:
lsof -n -p 14121 | grep TCPv6
java 13161 cache 143u sock 0,9 0t0 3617991518 protocol: TCPv6
java 13161 cache 180u sock 0,9 0t0 3618027565 protocol: TCPv6
java 13161 cache 184u sock 0,9 0t0 3618017649 protocol: TCPv6
此套接字在 运行 应用程序时永远不会关闭。
如何正确执行它?
我通过从 handle
方法中抛出 IOException
解决了这个问题。
try (final OutputStream os = httpExchange.getResponseBody()) {
IOUtils.copy(is, os, 128);
} catch (IOException e) {
logger.warn("Error proxy data", e);
throw e;
}
这个想法是从方法契约中得到的
我有下一个代理处理程序(使用嵌入式 HttpServer 和 Jersey 客户端):
@Override
public void handle(HttpExchange httpExchange) throws IOException {
....
....
WebTarget webTarget = httpClientHolder.getClient().target(url);
try (Response response = webTarget.request().get()) {
if (response.getStatusInfo().getStatusCode() != HttpStatus.SC_OK) {
logger.warn("Failed to download resource, url = " + url + " code = " +
response.getStatusInfo().getStatusCode());
throw new HttpException("Not found", 404);
}
httpExchange.sendResponseHeaders(200, response.getLength());
final InputStream is = response.readEntity(InputStream.class);
try (final OutputStream os = httpExchange.getResponseBody()) {
IOUtils.copy(is, os, 128);
} catch (IOException e) {
logger.warn("Error proxy data", e);
}
}
....
}
有时会发生以下情况
Error proxy data
java.io.IOException: Broken pipe
at sun.nio.ch.FileDispatcherImpl.write0(Native Method) ~[?:1.8.0_191]
at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:47) ~[?:1.8.0_191]
at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93) ~[?:1.8.0_191]
at sun.nio.ch.IOUtil.write(IOUtil.java:65) ~[?:1.8.0_191]
at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471) ~[?:1.8.0_191]
at sun.net.httpserver.Request$WriteStream.write(Request.java:391) ~[?:1.8.0_191]
at sun.net.httpserver.FixedLengthOutputStream.write(FixedLengthOutputStream.java:78) ~[?:1.8.0_191]
at sun.net.httpserver.PlaceholderOutputStream.write(ExchangeImpl.java:444) ~[?:1.8.0_191]
at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:2315) ~[cache-1.0-SNAPSHOT-jar-with-dependencies.jar:?]
at org.apache.commons.io.IOUtils.copy(IOUtils.java:2270) ~[cache-1.0-SNAPSHOT-jar-with-dependencies.jar:?]
at server.TimelineVttHandler.handle(TimelineVttHandler.java:129) [cache-1.0-SNAPSHOT-jar-with-dependencies.jar:?]
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79) [?:1.8.0_191]
at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83) [?:1.8.0_191]
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:82) [?:1.8.0_191]
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:675) [?:1.8.0_191]
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79) [?:1.8.0_191]
at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:647) [?:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_191]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_191]
Suppressed: java.io.IOException: insufficient bytes written to stream
at sun.net.httpserver.FixedLengthOutputStream.close(FixedLengthOutputStream.java:89) ~[?:1.8.0_191]
at sun.net.httpserver.PlaceholderOutputStream.close(ExchangeImpl.java:454) ~[?:1.8.0_191]
at server.TimelineVttHandler.handle(TimelineVttHandler.java:130) [cache-1.0-SNAPSHOT-jar-with-dependencies.jar:?]
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79) [?:1.8.0_191]
at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83) [?:1.8.0_191]
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:82) [?:1.8.0_191]
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:675) [?:1.8.0_191]
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79) [?:1.8.0_191]
at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:647) [?:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_191]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_191]
这对客户端来说不是什么大问题,但资源仍然存在,我们可以通过 lsof 查看:
lsof -n -p 14121 | grep TCPv6
java 13161 cache 143u sock 0,9 0t0 3617991518 protocol: TCPv6
java 13161 cache 180u sock 0,9 0t0 3618027565 protocol: TCPv6
java 13161 cache 184u sock 0,9 0t0 3618017649 protocol: TCPv6
此套接字在 运行 应用程序时永远不会关闭。 如何正确执行它?
我通过从 handle
方法中抛出 IOException
解决了这个问题。
try (final OutputStream os = httpExchange.getResponseBody()) {
IOUtils.copy(is, os, 128);
} catch (IOException e) {
logger.warn("Error proxy data", e);
throw e;
}
这个想法是从方法契约中得到的