我们正在使用JDK11 java.net.http
HTTP客户端从API获取数据。收到响应后,连接将在服务器中以TCP状态CLOSE_WAIT
保持打开状态,这意味着客户端必须关闭连接。
使用RFC 793术语:
CLOSE-WAIT-代表等待连接终止请求 来自本地用户。
这是我们的客户端代码,它作为无状态REST API在Java 12上运行的WildFly 16上运行。我们不明白为什么会这样。
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
public class SocketSandbox {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newBuilder().version(Version.HTTP_1_1).build();
try (var listener = new ServerSocket(59090)) {
System.out.println("Server is running...");
while (true) {
try (var socket = listener.accept()) {
HttpRequest request = HttpRequest
.newBuilder(URI.create("<remote_URL>"))
.header("content-type", "application/json").build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
var out = new PrintWriter(socket.getOutputStream(), true);
out.println(String.format("Response HTTP status: %s", response.statusCode()));
}
}
}
}
}
我们获得了“状态码”,表示已处理了http响应。
使用相同的代码调用其他端点时,连接正常。我们正在调用的远程API似乎是一个特殊的问题,但我们仍然不明白为什么Java HTTP客户端会保持连接打开。
我们尝试了Windows和Linux机器,甚至尝试了WildfFly之外的机器,但结果相同。在收到每个请求之后,即使从我们的无状态客户端进行处理并接收到响应,每个请求都保留为CLOSE_WAIT
,并且永远不会关闭。
如果我们关闭Java进程,连接将消失。
HTTP客户端发送的标头:
connection: 'Upgrade, HTTP2-Settings','content-length': '0',
host: 'localhost:3000', 'http2-settings': 'AAEAAEAAAAIAAAABAAMAAABkAAQBAAAAAAUAAEAA',
upgrade: 'h2c',
user-agent': 'Java-http-client/12'
服务器返回标头为Connection: close
我们试图微调实现类jdk.internal.net.http.ConnectionPool
中的池参数。
它没有解决问题。
System.setProperty("jdk.httpclient.keepalive.timeout", "5"); // seconds
System.setProperty("jdk.httpclient.connectionPoolSize", "1");
使用 Apache HTTP ,连接可以保持CLOSE_WAIT状态约90秒钟,但在此时间之后仍可以连接。
调用方法HttpGet.releaseConnection()
强制立即关闭连接。
HttpClient client = HttpClients.createDefault();
HttpGet get = new HttpGet("https://<placeholderdomain>/api/incidents/list");
get.addHeader("content-type", "application/json");
HttpResponse response = client.execute(get);
// This right here did the trick
get.releaseConnection();
return response.getStatusLine().getStatusCode();
使用 OkHttp 客户端,它可以按预期工作,开箱即用,没有连接中断。
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://<placeholderdomain>/grb/sb/incidentes/listar")
.header("content-type", "application/json").build();
Response response = client.newCall(request).execute();
return response.body().string();
我们仍在寻找如何使其在java-http-client中运行的方法,以便我们不必重写代码。
答案 0 :(得分:2)
我不建议为每个新请求创建一个新客户端。这违反了HTTP / 2的目的,HTTP / 2允许在单个连接上多路复用请求。
第二件事是这两个属性:
System.setProperty("jdk.httpclient.keepalive.timeout", "5"); // seconds
System.setProperty("jdk.httpclient.connectionPoolSize", "1");
仅适用于HTTP / 1.1连接,不适用于HTTP / 2。还请注意,这些属性在类加载时仅读取一次。所以设置它们之后
java.net.http
类已加载将无效。
最后,在释放HttpClient
之后,可能需要一些时间才能关闭所有保持活动的连接-这样做的内部机制基本上是依赖于GC的-这对于短暂的HttpClients来说不是很友好。 / p>
答案 1 :(得分:0)
已提交并确认为实施中的错误。