Завершение работы ExecutorService в API загрузки с пружинной загрузкой - PullRequest
2 голосов
/ 21 июня 2019

Создаю приложение api с весенней загрузкой, развернутое на weblogic 12c. Одно из моих требований - запускать длительные задачи для каждого входящего запроса. Входящий запрос отдыха может привести к выполнению нескольких асинхронных задач.

Поскольку меня не волнует ни ответ, ни какие-либо исключения, которые могут возникнуть в результате этих задач, я решил использовать ExecutorService, а не Callable или CompletableFuture.

ExecutorService executorService =
  Executors.newFixedThreadPool(2, new CustomizableThreadFactory("-abc-"));

Затем для входящего запроса, который я получаю в контроллере, запустите два цикла for и назначьте эти задачи ExecutorService:

for (final String orderId : orderIds) {
        for (final String itemId : itemIds) {               
            exec.execute(new Runnable() {
                public void run() { 
                    try {           
                         //call database operation
                    }catch(Throwable t) {
                       logger.error("EXCEPTION with {} , {}" ,orderId,itemId 
                    )
                }   
            });
        }//for          
    }//for

Мой вопрос касается закрытия службы ExecutorService. Мне известно о постепенном завершении работы (shutdown), гибридном завершении работы (awaitTermination) или внезапном завершении работы (shutdownNow)

каков предпочтительный подход между этими тремя приложениями для остальных API?

также существует ли ограничение на количество создаваемых пулов потоков, а именно количество создаваемых пулов потоков ExecutorService будет зависеть от количества входящих запросов

Ответы [ 2 ]

2 голосов
/ 21 июня 2019

В настоящее время у нас аналогичные требования, эту проблему трудно решить, поскольку вы хотите использовать правильный молоток, если хотите. Существуют очень тяжелые решения для организации длительных процессов, например SpringBatch.

Во-первых, не останавливайтесь и не запускайте ExecutorService. Основная задача этого класса - снять с вас бремя управления потоками, поэтому вам не нужно создавать и останавливать потоки самостоятельно. Так что вам не нужно управлять менеджером.

Но будьте осторожны с вашим подходом. Без использования очередей или другого метода балансировки нагрузки для разумного балансирования длительных процессов между экземплярами в вашем приложении. Или управляя тем, что происходит, когда Поток умирает, вы можете попасть в мир неприятностей. В целом, я бы сказал, что в настоящее время не имеет особого смысла взаимодействовать напрямую с Threads или ThreadPools и использовать решения более высокого уровня для решения проблем такого типа.

1 голос
/ 21 июня 2019

awaitTermination обычно немного безопаснее, в то время как shutdownNow более принудительно. Обычно хорошей идеей является использование awaitTermination в функциональном методе или даже в рабочем режиме, если вы хотите, чтобы исполнитель закрылся как можно скорее, но только после того, как он выполнил все, что было создано. Другими словами, когда нет активных задач, которые выполняет исполнитель. Ex.)

ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors);
Observable.of(items).schedule(Schedulers.from(executor)).flatMap(item -> {
   ... // this block represents a task that the executor will execute in a worker thread
}).onSubscribe(onNext -> 
   logItem(onNext), throwable -> 
   throwable.printStackTrace(), /* onComplete */ () -> 
   executor.awaitTermination(60, TimeUnit.Seconds)
);
... // you need to shutdown asap because these other methods below are also doing some computation/io-intensive stuff

Теперь, когда этот метод завершится, он вызовет awaitTermination, который либо немедленно закроет пул, если он не выполняет никаких задач, либо подождет до 60 секунд, если задачи все еще выполняются.

Потоки или работники перестают быть активными в течение 60 секунд бездействия в большинстве случаев, так как обычно это значение по умолчанию.

С другой стороны, если вы хотите, чтобы задачи перестали выполняться, как только (для некоторых примеров) было сгенерировано исключение, произошло нарушение безопасности или произошел сбой другого модуля / службы, вы можете использовать shutdownNow () немедленно остановить все задачи без возможности ожидания.

Мой совет по выбору между этими двумя вариантами: использовать shutdownNow в блоке перехвата, если вы не хотите, чтобы задачи продолжали выполняться, если есть исключение, т. Е. Больше нет причин возвращать список элементов для клиента, учитывая, что один из элементов не был добавлен в список. В противном случае, я бы рекомендовал использовать awaitTermination после вашего try-catch, установленного на одну минуту, для безопасного закрытия пула потоков, как только он выполнил все задачи, которые вы ему дали. Но делайте это только в том случае, если вы знаете, что исполнитель не будет нести ответственность за выполнение каких-либо дополнительных задач в будущем.

Простой shutdown, если это вариант для вас, также является хорошим методом. shutdown будет отклонять все входящие задачи, но ждать, пока текущие задачи не будут завершены, согласно документации Oracle.

Если вы не уверены, когда вам нужно закрыть исполнителя, было бы неплохо использовать метод @PreDestroy, чтобы исполнитель выполнял незадолго до вызова метода destroy для вашего компонента:

@PreDestroy
private void cleanup(){
   executor.shutdown();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...