ExecutorService: вызов future.get(long, TimeUnit) не приводит к запуску Callable в очереди

Я пытаюсь реализовать асинхронный преобразователь DNS, вызывая все подпрограммы, выполняющие DNS-запрос, в отдельном потоке с помощью ThreadPoolExecutor.

Я определяю вызываемый объект следующим образом:

public class SocketAddressCreator extends DnsCallable<String, InetSocketAddress> {
    private static final Logger log = Logger.getLogger(SocketAddressCreator.class);

    private int port;
    public SocketAddressCreator(String host, int port) {
        super(host);
        this.port = port;
    }
    public InetSocketAddress call() throws Exception {
        log.info("Starting to resolve. Host is: " + target + " .Port is: " + port);
        long start = System.currentTimeMillis();

        **InetSocketAddress addr = new InetSocketAddress(target, port);**

        log.info("Time waiting: " + (System.currentTimeMillis() - start));

        return addr;
    }
}

По сути, вызываемый объект попытается преобразовать имя хоста в InetAddress.

Затем я определяю ExecutorService:

executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
                    public Thread newThread(Runnable r) {
                        Thread t = Executors.defaultThreadFactory()
                                .newThread(r);
                        t.setName("DnsResolver");
                        t.setDaemon(true);
                        return t;
                    }
                });

И я отправляю Callable задачу:

    ..............
    **Future<V> f = executor.submit(task);**

    try {
        log.info("Query will be made");
        log.info("Queue size: " + executor.getQueue().size());
        **result = f.get(timeout, TimeUnit.MILLISECONDS);**
        log.info("Queue size: " + executor.getQueue().size());
        log.info("Query is finished");
    } catch (TimeoutException e) {
        boolean isCancelled = f.cancel(true);
        log.info("Task was cancelled: " + isCancelled);
        log.info("Queue size: " + executor.getQueue().size());
        ..........
    }
    ..............

Потом смотрю логи которые выкидывает моя программа и они довольно странные. Здесь у меня есть тайм-аут в разрешении DNS:

DnsResolver : Queue size: 1
DnsResolver : Task was cancelled: true
DnsResolver : Queue size: 1

Итак, после отправки моего вызываемого объекта, но до вызова future.get(long, TimeUnit) размер очереди равен 1. Но для меня это нормально. Однако после того, как я поймаю TimeoutException и отменю Future, размер очереди останется прежним (один). В моей программе есть только один поток, который отправляет вызываемые задачи в ExecutorService, и тот же поток также будет получать результаты. Более того, здесь есть еще более странная проблема: метод Callable.call() не вызывается, потому что, если бы он был вызван, я бы получил сообщение журнала:

log.info("Starting to resolve. Host is: " + target + " .Port is: " + port);

Итак, как метод future.get(long, TimeUnit) может генерировать TimeoutException, когда Callable не вызывается?


person Alex    schedule 16.04.2013    source источник


Ответы (1)


Следующие вызовы выполняют DNS-запросы: 1/ new InetSocketAddress(String, int) — поиск имени 2/ InetAddress.getByName(String) — поиск имени 3/ InetAddress.getHostName() — обратный поиск имени

НЕПРЕРЫВНАЯ блокировка звонков!

Как я уже говорил, я использую пул потоков, состоящий из одного потока. Я не понял, что необходимо иметь несколько потоков

Поэтому, если я поймаю исключение TimeoutException из вызова future.get(long, TimeUnit) и после этого попытаюсь отменить выполняемые задачи, вызвав future.cancel(boolean) ... Я не останавливаю единственный работающий поток от того, что он делает.

Я пытаюсь смоделировать длительный DNS-запрос и изменил resolv.conf следующим образом: nameserver X.X.X.X // у этого адреса нет действительного DNS-сервера! options timeout:30 Я хочу, чтобы DNS-клиент заблокировался на некоторое время, прежде чем вернуть мне отрицательный/положительный ответ.

Я провел нагрузочное тестирование своего приложения и... это полная катастрофа! Это потому, что у меня есть один поток, который разрешает эти DNS-запросы, и вызов future.get(long, TimeUnit) не останавливает его!

Конечно, я могу увеличить размер пула потоков. Я сделал это, и это решает мою проблему. Но... кажется глупым иметь более одного потока в моем пуле для разрешения этих DNS-запросов, потому что есть только один поток, который отправляет Callables, которые должны разрешать DNS-запросы, и тот же поток также получит результаты. .

person Alex    schedule 17.04.2013