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.
From RFC 793 terminology:
CLOSE-WAIT - represents waiting for a connection termination request from the local user.
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()));
}
}
}
}
}
We get the "status code" meaning the http response was processed.
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.
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.
Connections will disappear if we shutdown the Java process.
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'
Server returns response with header: Connection: close
We tried to fine-tune the pool parameters in implementation class jdk.internal.net.http.ConnectionPool
.
It did not solve the problem.
System.setProperty("jdk.httpclient.keepalive.timeout", "5"); // seconds
System.setProperty("jdk.httpclient.connectionPoolSize", "1");
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.
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();
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();
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.
To avoid close_wait, you need to make sure your server does not close the connection after it sends back the response because whomever disconnects first get stuck in close_wait and time_wait. So, if your server is getting stuck in close_wait it tells me that it is disconnecting after it sends the response.
HttpClient is fully thread-safe when used with a thread-safe connection manager such as MultiThreadedHttpConnectionManager.
You do not need to explicitly close the HttpClient, however, (you may be doing this already but worth noting) you should ensure that connections are released after method execution. Edit: The ClientConnectionManager within the HttpClient is going to be responsible for maintaining the state of connections.
CLOSE_WAIT - Indicates that the server has received the first FIN signal from the client and the connection is in the process of being closed. This means the socket is waiting for the application to execute close() . A socket can be in CLOSE_WAIT state indefinitely until the application closes it.
The issue is this: releasing the connection makes it available again to the HttpClient instance, but does not close it, because HTTP 1.1 is used and it can pipeline further requests to the same host:port in the same connection.
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. Calling method HttpGet.releaseConnection () force the connection close immediately.
What happens next is that when the HttpClient instance goes out of scope, it becomes available to the GC, but it will not be garbage collected immediately. Until the GC collects it, the socket connection held internally will stay open and the socket will be stuck in the CLOSE_WAIT state. before executing the method.
‘CLOSE_WAIT’ state on tcp connections occurs if the system has not received a close system call from the application, after having received notification (‘FIN’ packet) from the other system that it has closed its endpoint.
Submitted and confirmed as a bug in the implementation.
https://bugs.openjdk.java.net/browse/JDK-8221395
Update
Check the JIRA issue, it's fixed in JDK 13, and backported to 11.0.6. (Not sure about 12)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With