一种解决方案是在不同的线程上执行 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 dnsTimeout) throws IOException {
try {
return TimeSliceExecutor.execute(() -> InetAddress.getByName(host), dnsTimeout);
}
catch (TimeSliceExecutor.TimeSliceExecutorException ex) {
throw new UnknownHostException(host);
}
}
参考:https://stackoverflow.com/a/70610305/225217