Каков рекомендуемый способ порождения потоков из сервлета в Tomcat?
Вероятно, повторить! Я использую Tomcat в качестве своего сервера и хочу знать, как лучше всего создавать потоки в сервлете с детерминированными результатами. Я запускаю несколько длительных обновлений из сервлетного действия и хотел бы, чтобы запрос завершился и обновления происходили в фоновом режиме. Вместо добавления промежуточного программного обеспечения для обмена сообщениями, такого как RabbitMQ, я подумал, что смогу создать поток, который мог бы работать в фоновом режиме и завершаться в свое время. Я прочитал в других потоках SO, что сервер завершает потоки, порожденные сервером, для того, чтобы он хорошо управлял ресурсами.
Есть ли рекомендуемый способ порождения потоков, фоновых заданий при использовании Tomcat. Я также использую Spring MVC для приложения.
6 ответов
Ваша самая безопасная ставка - использовать пул потоков широкого применения с максимальным количеством потоков, так что задачи будут поставлены в очередь при необходимости. ExecutorService
очень помогает в этом.
При запуске приложения или инициализации сервлета используйте класс Executors:
executor = Executors.newFixedThreadPool(10); // Max 10 threads.
Затем во время обслуживания сервлета (вы можете проигнорировать результат для случая, который вас не интересует):
Future<ReturnType> result = executor.submit(new CallableTask());
Наконец, во время завершения работы приложения или уничтожения сервлета:
executor.shutdownNow(); // Returns list of undone tasks, for the case that.
Вы можете использовать реализацию CommonJ WorkManager (JSR 237), например, Foo-CommonJ:
CommonJ - JSR 237 Timer & WorkManager
Foo-CommonJ - это реализация JSR 237 Timer и WorkManager. Он предназначен для использования в контейнерах, которые не имеют собственной реализации - в основном это простые контейнеры сервлетов, такие как Tomcat. Он также может быть использован на полностью запущенных серверах приложений Java EE, которые не имеют API WorkManager или имеют нестандартный API, такой как JBoss.
Зачем использовать WorkManager?
Обычный вариант использования - сервлет или JSP должны объединять данные из нескольких источников и отображать их на одной странице. Выполнение собственных потоков в управляемой среде, такой как контейнер J2EE, неуместно и никогда не должно выполняться в коде уровня приложения. В этом случае API WorkManager может использоваться для параллельного извлечения данных.
Установить / Развернуть CommonJ
Развертывание ресурсов JNDI зависит от поставщика. Эта реализация поставляется с классом Factory, который реализует
javax.naming.spi.ObjectFactory
интерфейс с делает его легко развертываемым в самых популярных контейнерах. Он также доступен как сервис JBoss. Больше...
Обновление: просто чтобы прояснить, вот что пишет Утилиты параллелизма для Java EE Preview (похоже, это наследник JSR-236 и JSR-237) о неуправляемых потоках:
2.1 Контролируемые и неуправляемые потоки
Серверы приложений Java EE требуют управления ресурсами для централизации администрирования и защиты компонентов приложения от потребления ненужных ресурсов. Это может быть достигнуто путем объединения ресурсов и управления жизненным циклом ресурса. Использование утилит параллелизма Java SE, таких как
java.util.concurrency
API,java.lang.Thread
а такжеjava.util.Timer
в серверном прикладном компоненте, таком как сервлет или EJB, возникают проблемы, так как контейнер и сервер не знают об этих ресурсах.Расширяя
java.util.concurrent
API, серверы приложений и контейнеры Java EE могут узнавать об используемых ресурсах и предоставлять надлежащий контекст выполнения для асинхронных операций, с которыми нужно работать.Это в значительной степени достигается путем предоставления управляемых версий преобладающего
java.util.concurrent.ExecutorService
интерфейсы.
Так что ничего нового IMO, "старая" проблема та же, неуправляемые потоки все еще неуправляемые потоки:
- Они неизвестны серверу приложений и не имеют доступа к контекстной информации Java EE.
- Они могут использовать ресурсы на задней панели сервера приложений, и без какой-либо возможности администратора контролировать их количество и использование ресурсов, это может повлиять на способность сервера приложений восстанавливать ресурсы после сбоя или корректно завершать работу.
Рекомендации
Spring поддерживает асинхронную задачу (в вашем случае длительное выполнение) с помощью Spring-Scheduling. Вместо прямого использования потоков Java, я предлагаю использовать его с Quartz.
: ресурсы
Я знаю, что это старый вопрос, но люди продолжают его задавать, пытаясь все время делать подобные вещи (явным образом порождая потоки при обработке запроса сервлета)... Это очень ошибочный подход - по нескольким причинам... Просто заявить, что контейнеры Java EE не одобряются такой практикой, недостаточно, хотя в целом это так...
Самое главное, никогда нельзя предсказать, сколько одновременных запросов будет получать сервлет в любой момент времени. Веб-приложение, по определению сервлет, предназначено для одновременной обработки нескольких запросов на заданной конечной точке. Если вы программируете, вы запрашиваете логику обработки для явного запуска определенного количества параллельных потоков, вы рискуете столкнуться с практически неизбежной ситуацией, когда исчерпаны доступные потоки и задушено ваше приложение. Ваш исполнитель задач всегда настроен на работу с пулом потоков, который ограничен конечным разумным размером. Чаще всего он не превышает 10-20 (вам не нужно слишком много потоков, выполняющих вашу логику - в зависимости от характера задачи, ресурсов, за которые они конкурируют, количества процессоров на вашем сервере и т. Д.) Скажем так ваш обработчик запросов (например, метод контроллера MVC) вызывает один или несколько @Async-аннотированных методов (в этом случае Spring абстрагирует исполнителя задачи и облегчает вам задачу) или явно использует исполнителя задачи. Когда ваш код выполняется, он начинает захватывать доступные потоки из пула. Это хорошо, если вы всегда обрабатываете один запрос за раз без немедленных последующих запросов. (В этом случае вы, вероятно, пытаетесь использовать неправильную технологию для решения вашей проблемы.) Однако, если это веб-приложение, работающее с произвольными (или даже известными) клиентами, которые могут забивать конечную точку запросами, вы будете быстро истощите пул потоков, и запросы начнут накапливаться, ожидая доступности потоков. Только по этой причине вы должны понимать, что вы можете пойти по неверному пути - если вы рассматриваете такой дизайн.
Лучшим решением может быть поэтапная обработка данных, которые будут обрабатываться асинхронно (это может быть очередь, или любой другой тип временного / промежуточного хранилища данных), и возвращать ответ. Пусть внешнее, независимое приложение или даже несколько его экземпляров (развернутых вне вашего веб-контейнера) опрашивают промежуточные конечные точки и обрабатывают данные в фоновом режиме, возможно, используя конечное число параллельных потоков. Не только такое решение даст вам преимущество асинхронной / параллельной обработки, но также будет масштабироваться, потому что вы сможете запускать столько экземпляров такого обработчика, сколько вам нужно, и их можно распределять, указывая на конечную точку промежуточного хранения. НТН
Строго говоря, вы не можете создавать потоки в соответствии со спецификацией Java EE. Я также рассмотрел бы возможность отказа в обслуживании (преднамеренного или иного), если несколько запросов поступают одновременно.
Решение промежуточного программного обеспечения, безусловно, будет более надежным и совместимым со стандартами.
Начиная с Spring 3, вы можете использовать аннотации @Async:
@Service
public class smg {
...
@Async
public getCounter() {...}
}
с <context:component-scan base-package="ch/test/mytest">
а также <task:annotation-driven/>
в контекстном файле
Пожалуйста, обратитесь к этому учебнику: http://spring.io/blog/2010/01/05/task-scheduling-simplifications-in-spring-3-0/
Отлично работает для меня на Tomcat7, и вам не нужно управлять пулом потоков.