我们使用JDK11 java.net.http
HTTP从java.net.http
获取数据。在我们收到响应后,连接仍然使用TCP状态CLOSE_WAIT
打开在我们的服务器中,这意味着客户端必须关闭连接。
来自RFC 793术语:
关闭-等待-表示等待本地用户的连接终止请求.
这是我们的客户端代码,它以无状态REST的形式运行在Java12上的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客户端保持打开连接。
我们尝试了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 ,连接保持在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客户机中工作,这样我们就不必重写代码了。
发布于 2019-03-25 14:39:29
提交并确认为实现中的一个错误。
https://bugs.openjdk.java.net/browse/JDK-8221395
更新
检查JIRA问题,它在JDK 13中修复,并支持11.0.6。(不确定大约12)
发布于 2019-03-22 12:00:55
我不建议为每个新请求创建一个新客户端。这违背了HTTP/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来说并不是很友好。
https://stackoverflow.com/questions/55271192
复制相似问题