【问题标题】:How to set the timeout for socket when looking for host?寻找主机时如何设置socket的超时时间?
【发布时间】:2014-01-31 19:37:21
【问题描述】:

在我的单元测试中,我使用commons httpclient请求了一个不正确的远程网站,并且最大超时为:

@Test(timeout = 10000)
public void should_not_be_accessible_if_configuration_is_incorrect() throws Exception {
     // use httpclient to visit an invalid remote http web site
}

但在某些计算机中可能会抛出这样的异常:

java.lang.Exception: test timed out after 10000 milliseconds
at java.net.Inet4AddressImpl.lookupAllHostAddr(Native Method)
at java.net.InetAddress$1.lookupAllHostAddr(InetAddress.java:894)
at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1286)
at java.net.InetAddress.getAllByName0(InetAddress.java:1239)
at java.net.InetAddress.getAllByName(InetAddress.java:1155)
at java.net.InetAddress.getAllByName(InetAddress.java:1091)
at org.apache.http.impl.conn.SystemDefaultDnsResolver.resolve(SystemDefaultDnsResolver.java:44)
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:102)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:314)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:357)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:218)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:194)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:85)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:186)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)

似乎它阻塞了方法:java.net.Inet4AddressImpl.lookupAllHostAddr()

我想知道有没有办法为它设置最大超时?

【问题讨论】:

标签: java sockets timeout


【解决方案1】:

您可以通过here 中描述的超时属性控制 Java 的 DNS 超时:

com.example.jndi.dns.timeout.initial
com.example.jndi.dns.timeout.retries

【讨论】:

  • 这些 JNDI DNS 超时值是否对非 JNDI DNS 行为有任何影响,例如 Java 中标准 URLConnection 的行为?
【解决方案2】:

一种解决方案是在不同的线程上执行 DNS 解析,该线程只给定一定的时间来完成。

这里有一个简单的实用程序可以帮助您做到这一点:

public class TimeSliceExecutor {

  public static class TimeSliceExecutorException extends RuntimeException {
    public TimeSliceExecutorException(String message, Throwable cause) {
      super(message, cause);
    }
  }

  public static void execute(Runnable runnable, long timeoutInMillis) {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    try {
      Future<?> future = executor.submit(runnable);
      getFuture(future, timeoutInMillis);
    }
    finally {
      if (executor != null) {
        executor.shutdown();
      }
    }
  }

  public static <T> T execute(Callable<T> callable, long timeoutInMillis) {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    try {
      Future<T> future = executor.submit(callable);
      return getFuture(future, timeoutInMillis);
    }
    finally {
      if (executor != null) {
        executor.shutdown();
      }
    }
  }

  public static <T> T getFuture(Future<T> future, long timeoutInMillis) {
    try {
      return future.get(timeoutInMillis, TimeUnit.MILLISECONDS);
    }
    catch (InterruptedException ex) {
      Thread.currentThread().interrupt();
      throw new TimeSliceExecutorException("Interrupton exception", ex);
    }
    catch (ExecutionException ex) {
      throw new TimeSliceExecutorException("Execution exception", ex);
    }
    catch (TimeoutException ex) {
      throw new TimeSliceExecutorException(String.format("%dms timeout reached", timeoutInMillis), ex);
    }
  }
}

然后按照以下思路构建套接字:

private Socket buildSocket() throws IOException {
  final Socket socket = new Socket();
  socket.setSoTimeout(socketTimeout);
  socket.connect(new InetSocketAddress(resolveHost(host, dnsTimeout), port), connectionTimeout);
  return socket;
}

private static InetAddress resolveHost(String host, long timeout) throws IOException {
  try {
    return TimeSliceExecutor.execute(() -> InetAddress.getByName(host), timeout);
  }
  catch (TimeSliceExecutor.TimeSliceExecutorException ex) {
    throw new UnknownHostException(host);
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-31
    • 2013-06-11
    • 1970-01-01
    • 1970-01-01
    • 2017-08-28
    • 1970-01-01
    • 2018-02-27
    相关资源
    最近更新 更多