重复在临时端口启动服务器时的连接处理问题求助
在临时端口启动服务器时的连接处理问题求助
大家好,我现在遇到一个挺棘手的问题,想请各位帮忙分析下:
问题场景
我写了一段循环逻辑:每次迭代里构造并启动一个Jetty服务器,然后创建HTTP客户端发送请求,打印响应码后再清理掉服务器和客户端。但运行几千次迭代后,偶尔会出现连接超时或者其他IO错误,而且这个问题不仅在WireMock测试里出现,用纯Jetty也能复现,MacOS 15.6.1和Linux CI环境下都有这个情况。
已尝试的排查与优化措施
我已经针对常见的可能原因做了排查,但问题依然存在:
- 用
netstat -np tcp检查过临时端口的使用情况,确认是否耗尽 - 给Jetty连接器设置了
connector.setReuseAddress(true);,想开启SO_REUSEADDR来复用端口 - 在MacOS上通过
sudo sysctl -w net.inet.tcp.msl=1把TCP最大分段生存期改成1,减少TIME_WAIT状态的端口占用 - 怀疑是GC频繁导致的问题,特意增大了JVM堆内存,但现象没有改善
错误示例
连接超时错误
Exception in thread "main" java.net.http.HttpConnectTimeoutException: HTTP connect timed out at java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:949) at java.net.http/jdk.internal.net.http.HttpClientFacade.send(HttpClientFacade.java:133) at com.example.Main.main(Main.java:43) Caused by: java.net.http.HttpConnectTimeoutException: HTTP connect timed out at java.net.http/jdk.internal.net.http.ResponseTimerEvent.handle(ResponseTimerEvent.java:68) at java.net.http/jdk.internal.net.http.HttpClientImpl.purgeTimeoutsAndReturnNextDeadline(HttpClientImpl.java:1788) at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.run(HttpClientImpl.java:1386) Caused by: java.net.ConnectException: HTTP connect timed out at java.net.http/jdk.internal.net.http.ResponseTimerEvent.handle(ResponseTimerEvent.java:69) ... 2 more
EOF读取错误
Exception in thread "main" java.io.IOException: HTTP/1.1 header parser received no bytes at java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:970) at java.net.http/jdk.internal.net.http.HttpClientFacade.send(HttpClientFacade.java:133) at com.example.Main.main(Main.java:43) Caused by: java.io.IOException: HTTP/1.1 header parser received no bytes at java.net.http/jdk.internal.net.http.common.Utils.wrapWithExtraDetail(Utils.java:412) at java.net.http/jdk.internal.net.http.Http1Response$HeadersReader.onReadError(Http1Response.java:590) at java.net.http/jdk.internal.net.http.Http1AsyncReceiver.checkForErrors(Http1AsyncReceiver.java:302) at java.net.http/jdk.internal.net.http.Http1AsyncReceiver.flush(Http1AsyncReceiver.java:268) at java.net.http/jdk.internal.net.http.common.SequentialScheduler$LockingRestartableTask.run(SequentialScheduler.java:182) at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:149) at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:207) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) at java.base/java.lang.Thread.run(Thread.java:1583) Caused by: java.io.EOFException: EOF reached while reading at java.net.http/jdk.internal.net.http.Http1AsyncReceiver$Http1TubeSubscriber.onComplete(Http1AsyncReceiver.java:601) at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$ReadSubscription.signalCompletion(SocketTube.java:648) at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$InternalReadSubscription.read(SocketTube.java:853) at java.net.http/jdk.internal.net.http.SocketTube$SocketFlowTask.run(SocketTube.java:181) at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:207) at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:280) at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:233) at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$InternalReadSubscription.signalReadable(SocketTube.java:782)
复现代码(服务器部分)
下面是我用的服务器实现类(补全了部分截断的代码):
package com.example; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.util.Callback; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; public class SingleEndpointServer implements AutoCloseable { private final Server server; public SingleEndpointServer() { this.server = new Server(); ServerConnector connector = new ServerConnector(server); connector.setReuseAddress(true); // 尝试开启SO_REUSEADDR server.addConnector(connector); // 设置简单的请求处理器 server.setHandler(new Handler() { @Override public boolean handle(Request request, Response response, Callback callback) throws Exception { response.setStatus(200); response.getHeaders().add(HttpHeader.CONTENT_TYPE, "text/plain"); String body = "OK"; ByteBuffer buffer = StandardCharsets.UTF_8.encode(body); response.write(true, buffer, callback); return true; } }); } public void start() throws Exception { server.start(); // 获取实际绑定的临时端口 int port = ((ServerConnector) server.getConnectors()[0]).getLocalPort(); System.out.println("Server started on port " + port); } @Override public void close() throws Exception { server.stop(); server.destroy(); } }
目前我已经排查了端口复用、TIME_WAIT状态、GC等常见方向,但问题依然存在,想请教各位还有哪些可能的原因?或者有没有进一步排查的思路?谢谢大家!




