![](/img/trans.png)
[英]Connections leaking with state CLOSE_WAIT with java.net.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
我們嘗試微調實現類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 中工作,以便我們不必重寫代碼。
提交並確認為實現中的錯誤。
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.