我们正在使用 JDK11 java.net.http
HTTP 客户端从 API 获取数据。在我们收到响应后,连接在我们的服务器中仍然以 TCP 状态打开CLOSE_WAIT
,这意味着客户端必须关闭连接。
来自RFC 793术语:
CLOSE-WAIT - 表示等待来自本地用户的连接终止请求。
这是我们的客户端代码,它在 WildFly 16 上运行,在 Java 12 上作为无状态 REST API 运行。我们不明白为什么会这样。
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 中工作,这样我们就不必重写代码。