httpjava-11openjdk-11java-http-client

JDK 11 HttpClient: BindException: Cannot assign requested address


I am using the new HttpClient shipped with JDK 11 to make many requests (to Github's API, but I think that's irrelevant), especially GETs.

For each request, I build and use an HttpClient, like this:

final ExecutorService executor = Executors.newSingleThreadExecutor();
final HttpClient client = client = HttpClient
    .newBuilder()
    .followRedirects(HttpClient.Redirect.NORMAL)
    .connectTimeout(Duration.ofSeconds(10))
    .executor(executor)
    .build();
try {
   //send request and return parsed response;
} finally {
   //manually close the specified executor because HttpClient doesn't implement Closeable,
   //so I'm not sure when it will release resources.
   executor.shutdownNow();
}

This seems to work fine, except every now and then, I get the bellow exception and requests will not work anymore until I restart the app:

Caused by: java.net.ConnectException: Cannot assign requested address
...
Caused by: java.net.BindException: Cannot assign requested address
    at java.base/sun.nio.ch.Net.connect0(Native Method) ~[na:na]
    at java.base/sun.nio.ch.Net.connect(Net.java:476) ~[na:na]
    at java.base/sun.nio.ch.Net.connect(Net.java:468) ~[na:na]

Note that this is NOT the JVM_Bind case.

I am not calling localhost or listening on a localhost port. I am making GET requests to an external API. However, I've also checked the etc/hosts file and it seems fine, 127.0.0.1 is mapped to localhost.

Does anyone know why this happens and how could I fix it? Any help would be greatly appreciated.


Solution

  • You can try using one shared HttpClient for all requests, since it manages connection pool internally and may keep connections alive for same host (if supported). Performing a lot of requests on different HttpClients is not effective, because you'll have n thread pools and n connection pools, where n is an amount of clients. And they won't share underlying connections to the host.

    Usually, an application creates a single instance of HttpClient in some kind of main() and provides it as a dependency to users.

    E.g.:

    public static void main(String... args) {
      final HttpClient client = client = HttpClient
        .newBuilder()
        .followRedirects(HttpClient.Redirect.NORMAL)
        .connectTimeout(Duration.ofSeconds(10))
        .build();
      new GithubWorker(client).start();
    }
    

    Update: how to stop current client

    According to JavaDocs in internal private class of JDK in HttpClientImpl.stop method:

        // Called from the SelectorManager thread, just before exiting.
        // Clears the HTTP/1.1 and HTTP/2 cache, ensuring that the connections
        // that may be still lingering there are properly closed (and their
        // possibly still opened SocketChannel released).
        private void stop() {
            // Clears HTTP/1.1 cache and close its connections
            connections.stop();
            // Clears HTTP/2 cache and close its connections.
            client2.stop();
            // shutdown the executor if needed
            if (isDefaultExecutor) delegatingExecutor.shutdown();
        }
    

    This method is called from SelectorManager.showtdown (SelectorManager is created in HttpClient's constructor), where shutdown() method called in finally block around busy loop in SelectorManager.run() (yes, it implements Thread). This busy loop is while (!Thread.currentThread().isInterrupted()). So to enter this finally block you need to either fail this loop with exception or interrupt the running thread.