Как EJB может распараллелить длинный процесс, интенсивно использующий процессор?

Приложение имеет длительный процесс с интенсивным использованием ЦП, который в настоящее время выполняется последовательно на одном сервере (метод EJB), когда клиент запрашивает его.

Теоретически возможно (с концептуальной точки зрения) разделить этот процесс на N порций и выполнить их параллельно, при условии, что выходные данные всех параллельных заданий могут быть собраны и объединены перед отправкой обратно клиенту, который инициировал процесс, Я хотел бы использовать это распараллеливание для оптимизации производительности.

Как я могу реализовать это распараллеливание с EJB? Я знаю, что мы не должны создавать потоки в методе EJB. Вместо этого мы должны публиковать сообщения (по одному на задание), которые будут использоваться компонентами, управляемыми сообщениями (MDB). Но тогда это больше не будет синхронный вызов. И синхронность, по-видимому, является обязательным требованием в этом случае, так как мне нужно собрать выходные данные всех заданий, прежде чем отправить их обратно клиенту.

Есть ли решение для этого?

6 ответов

Решение

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

Используйте WorkManager из API Commonj. Он учитывает управляемые потоки в контейнере Java EE и специально разработан для вашего случая использования. Если вы используете WebSphere или WebLogic, эти API уже доступны на вашем сервере. Для других вам нужно будет внедрить стороннее решение в себя.

Информация WorkManager

Смежные вопросы Почему нерестить темы не рекомендуется

Есть разные способы сделать это.

Во-первых, вы можете использовать EJB Timer для создания однократного запуска процесса, который начнется немедленно. Это хорошая техника для порождения процессов в фоновом режиме. Таймер EJB связан с конкретной реализацией Session Bean. Вы можете добавить таймер EJB к каждому сессионному компоненту, который вы хотите сделать, или у вас может быть один сессионный компонент, который затем может вызывать логику вашего приложения через некоторый механизм диспетчеризации.

Для меня я передаю сериализуемый блоб параметров вместе с именем класса, которое соответствует определенному интерфейсу, универсальному Сессионному Бину, который затем выполняет класс. Таким образом, я могу легко справиться с чем угодно.

Одно предупреждение о таймере EJB заключается в том, что таймеры EJB являются постоянными. После создания EJB-таймер остается в контейнере, пока его работа не будет завершена или отменена. Причиной этого является то, что если у вас длительный процесс, а сервер отключается, то при перезапуске процесс продолжится и возобновит работу. Помните, что это может быть хорошо, но только если ваш процесс готов к перезапуску. Но если у вас есть простой процесс, повторяющий "10000 элементов", если сервер отключается на 9999 элементах, когда он возвращается, вы можете легко увидеть, что он просто начинается с первого элемента. Это все выполнимо, просто предостережение из.

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

Самое умное здесь, и кое-что, что я также сделал, используя работу с Timer Bean, заключается в том, что вы можете контролировать, сколько "заданий" будет выполняться, исходя из того, сколько экземпляров MDB вы настроили в системе.

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

Это на самом деле работает на удивление хорошо. Есть небольшие накладные расходы, которые разделяют процесс и направляют его через очередь JMS, но это все, по сути, "время запуска". Как только это начнется, вы получите реальную выгоду.

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

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

Мне было легко настроить инфраструктуру, позволяющую запускать задачи либо на EJB Timer, либо в очереди разброса MDB, задачи одинаковы, и я мог отслеживать их выполнение, останавливать их и т. Д.

Вы можете комбинировать технику разброса, чтобы создать несколько заданий EJB Timer. Одним из бесплатных преимуществ MDB является то, что он действует как пул потоков, который может ограничивать ваши задания (поэтому вы не переполняете свою систему слишком большим количеством фоновых процессов). Вы получаете это "бесплатно", просто используя возможности управления EJB в контейнере.

Наконец, в Java EE 6 появился новый "асинхронный" (или что-то еще) квалификатор для методов Session Bean. Я не знаю подробностей о том, как это работает, поскольку мне еще предстоит поиграть с новым контейнером Java EE 6. Но я предполагаю, что вы, вероятно, не захотите менять контейнеры только для этого объекта.

EJB - это, в конечном счете, транзакционный компонент для системы клиент-сервер, обеспечивающий семантику запроса / ответа. Если вы оказались в положении, в котором вам нужно провести длительную транзакцию в рамках цикла запрос / ответ, то где-то ваш системный архитектор (ure) принял неправильный поворот.

Описанная вами ситуация четко и корректно обрабатывается архитектурой, основанной на событиях, с серверной частью обмена сообщениями. Начальное событие инициирует процесс (который затем может быть тривиально распараллелен, если рабочие подписываются на тему события), а сам процесс агрегации вызывает событие по его завершении. Вы по-прежнему можете сжать эти последовательности в рамках цикла запрос / ответ, но при необходимости вы нарушите букву и дух спецификаций архитектуры системы Java EE.

Назад в будущее - Java EE 7 имеет гораздо больше поддержки параллелизма через ManagedThreadFactory, службу ManagedExecutor и т. Д. (JSR 236: Утилиты параллелизма для Java EE), с помощью которых вы можете создавать свои собственные "управляемые" потоки. Это больше не является табу в EE AS поддерживая его (Wildfly?) через использование API ManagedThread *

Подробнее

https://jcp.org/aboutJava/communityprocess/ec-public/materials/2013-01-1516/JSR236-EC-F2F-Jan2013.pdf http://docs.oracle.com/javaee/7/tutorial/doc/concurrency-utilities002.htm

Однажды я участвовал в проекте, где транзакции EJB выполнялись до 5 часов одновременно. Aargh!

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

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

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

Вы достаточно хорошо проанализировали ситуацию, и нет, для этого нет паттерна, соответствующего модели EJB.

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

Я работал над проектом с подобными требованиями, и я решил создать дополнительные темы (в отличие от sepc). Операция распараллеливания была доступна только для чтения, поэтому она работала в отношении транзакции (поток, по сути, не имел бы транзакцию, связанную с ними). Я также знал, что не буду создавать слишком много потоков на вызовы EJB, поэтому количество потоков не было проблемой. Но если ваши потоки должны изменять данные, то вы серьезно нарушаете транзакционную модель EJB. Но если ваша работа в чистых вычислениях, это может быть хорошо.

Надеюсь, поможет...

Другие вопросы по тегам