与HttpClient的状态为CLOSE_WAIT的连接泄漏 [英] Connections leaking with state CLOSE_WAIT with HttpClient

查看:250
本文介绍了与HttpClient的状态为CLOSE_WAIT的连接泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在使用JDK11 java.net.http HTTP客户端从API获取数据.收到响应后,连接将在服务器中以TCP状态CLOSE_WAIT保持打开状态,这意味着客户端必须关闭连接.

We are using JDK11 java.net.http HTTP client to get data from an API. After we receive the response the connections remain opened in our server with TCP state CLOSE_WAIT, which means the client must close the connection.

来自 RFC 793 术语:

CLOSE-WAIT-代表等待连接终止请求 来自本地用户.

CLOSE-WAIT - represents waiting for a connection termination request from the local user.

这是我们的客户端代码,它作为无状态REST API在Java 12上运行的WildFly 16上运行.我们不明白为什么会这样.

This is our client code which runs on WildFly 16 running on Java 12 as a stateless REST API. We don't understand why this is happening.

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响应.

We get the "status code" meaning the http response was processed.

使用相同的代码调用其他端点时,连接正常.我们正在调用的远程API似乎是一个特殊的问题,但是我们仍然不明白为什么Java HTTP客户端会保持打开连接的状态.

When using the same code to call other endpoints connections are fine. It seems to be a particular issue with the remote API we are calling, but still we don't understand why the Java HTTP client is keeping connections opened.

我们尝试了Windows和Linux计算机,甚至尝试了WildfFly之外的独立计算机,但结果都是一样的.每次请求后,即使从我们的无状态客户端执行请求并接收到响应,每个请求也都保留为CLOSE_WAIT,并且永远不会关闭.

We tried both Windows and Linux machines, and even standalone outside of WildfFly, but the same result happens. After each request, even doing it from our stateless client and receiving the response, each one is left as CLOSE_WAIT and never close.

如果我们关闭Java进程,连接将消失.

Connections will disappear if we shutdown the Java process.

HTTP客户端发送的标头:

Headers that are sent by the HTTP client:

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中微调池参数.

We tried to fine-tune the pool parameters in implementation class 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秒钟,但在此时间之后仍可以连接.

Update (2)

With Apache HTTP the connections l getting left in CLOSE_WAIT state for about 90 seconds, but it is able to the connections after that time.

调用方法HttpGet.releaseConnection()强制立即关闭连接.

Calling method HttpGet.releaseConnection() force the connection close immediately.

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 客户端,它可以按预期工作,开箱即用,没有连接卡死.

And with OkHttp client it worked as expected out of the box, no connections stuck.

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中运行的方法,以便我们不必重写代码.

We are still trying to find how to make it work in java-http-client so that we don't have to rewrite the code.

推荐答案

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

Submitted and confirmed as a bug in the implementation.

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

更新

检查JIRA问题,该问题已在JDK 13中修复,并反向移植到11.0.6. (不确定12)

Check the JIRA issue, it's fixed in JDK 13, and backported to 11.0.6. (Not sure about 12)

这篇关于与HttpClient的状态为CLOSE_WAIT的连接泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆