JDK 11 HttpClient:BindException:无法分配请求的地址

ami*_*mil 5 http java-http-client java-11 openjdk-11

我正在使用 JDK 11 附带的新 HttpClient 发出许多请求(向 Github 的 API,但我认为这无关紧要),尤其是 GET。

对于每个请求,我构建并使用一个 HttpClient,如下所示:

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();
}
Run Code Online (Sandbox Code Playgroud)

这似乎工作正常,除了时不时地,我收到以下异常,并且请求将不再有效,直到我重新启动应用程序:

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]
Run Code Online (Sandbox Code Playgroud)

请注意,这不是JVM_Bind情况。

我没有调用本地主机或侦听本地主机端口。我正在向外部 API 发出 GET 请求。但是,我也检查了该etc/hosts文件,看起来很好,127.0.0.1已映射到localhost.

有谁知道为什么会发生这种情况以及我该如何解决它?任何帮助将不胜感激。

Kir*_*ill 1

您可以尝试HttpClient对所有请求使用一个共享连接,因为它在内部管理连接池,并且可以使同一主机的连接保持活动状态(如果支持)。在不同的服务器上执行大量请求HttpClient是无效的,因为您将拥有n线程池和n连接池,其中n有大量的客户端。并且它们不会共享与主机的底层连接。

通常,应用程序会创建HttpClient某种类型的单个实例main(),并将其作为依赖项提供给用户。

例如:

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();
}
Run Code Online (Sandbox Code Playgroud)

更新:如何停止当前客户端

根据JavaDocs中JDK内部私有类的HttpClientImpl.stop方法:

    // 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();
    }
Run Code Online (Sandbox Code Playgroud)

该方法是从SelectorManager.showtdown(在的构造函数SelectorManager中创建的)调用的,其中方法在繁忙循环周围的块中调用(是的,它实现了)。这个繁忙的循环是. 因此,要进入此块,您需要使此循环因异常而失败或中断正在运行的线程。HttpClientshutdown()finallySelectorManager.run()Threadwhile (!Thread.currentThread().isInterrupted())finally