簡體   English   中英

連接泄漏,狀態為 CLOSE_WAIT 與 HttpClient

[英]Connections leaking with state CLOSE_WAIT with HttpClient

我們使用 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

更新 (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 中工作,以便我們不必重寫代碼。

提交並確認為實現中的錯誤。

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

更新

檢查 JIRA 問題,它已在 JDK 13 中修復,並已向后移植到 11.0.6。 (不確定12個)

我不建議為每個新請求創建一個新客戶端。 這違背了 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 不是很友好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM