连接状态CLOSE_WAIT与HttpClient泄漏

问题描述 投票:3回答:2

我们使用JDK11 java.net.http HTTP客户端从API获取数据。收到响应后,连接仍在我们的服务器中打开,状态为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进程,连接将消失。

enter image description here

HTTP客户端发送的标头:

connection: 'Upgrade, HTTP2-Settings','content-length': '0',
host: 'localhost:3000', 'http2-settings': 'AAEAAEAAAAIAAAABAAMAAABkAAQBAAAAAAUAAEAA',
upgrade: 'h2c',
user-agent': 'Java-http-client/12'

服务器返回带标题的响应:Connection: close

更新(1)

我们试图在实现类jdk.internal.net.http.ConnectionPool中微调池参数。

它没有解决问题。

System.setProperty("jdk.httpclient.keepalive.timeout", "5"); // seconds
System.setProperty("jdk.httpclient.connectionPoolSize", "1");

更新(2)

使用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中工作,这样我们就不必重写代码了。

java java-11 connection-leaks java-http-client
2个回答
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来说并不是非常友好。


0
投票

提交并确认为实施中的错误。

https://bugs.openjdk.java.net/browse/JDK-8221395

© www.soinside.com 2019 - 2024. All rights reserved.