Параллельная обработка ввода-вывода JAVA EE: лучший метод?

У меня есть приложение JavaEE 6/EJB3.1/Glassfish 3.1.2, которое извлекает страницы .xml с удаленного компьютера, преобразует их в объекты java, а затем сохраняет каждый из них в моей базе данных mysql. Есть десятки тысяч этих страниц .xml, и я просто добавляю их постепенно.
Это работает отлично, за исключением того, что это очень медленно (70 мс извлечение страницы + небольшое количество времени для преобразования и сохранения объектов).

Я хочу выполнять эту работу одновременно, чтобы ускорить ее - каков наилучший способ?

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

Мое исследование до сих пор сузило его до двух возможностей (не стесняйтесь добавлять другие):

  1. Компоненты, управляемые сообщениями. Я предполагаю, хотя, вероятно, ошибаюсь, что сеансовый компонент будет отправлять сообщения URL-адресов до тех пор, пока очередь сообщений не будет заполнена (скажем, будет добавлено 10 URL-адресов), а затем блокируется до тех пор, пока очередь не будет заполнена. полный. Glassfish создаст 10 экземпляров компонента сообщений, который я создаю, каждый из которых получит один из .xml с одного из URL-адресов, обновит счетчик OAuth, а затем отправит этот .xml как сообщение в другую очередь с другим компонентом сообщения, который преобразуется и сохраняется. xmls из этой очереди.
  2. Использовать метод @Asynchronous и создать свою собственную потокобезопасную очередь? Это может быть намного проще и больше подходит для того, что я делаю, но я точно не знаю, как бы я это реализовал.

Любой совет будет принят во внимание!


person Tim    schedule 21.11.2012    source источник


Ответы (2)


Поскольку вы имеете дело с удаленным сервером, знаете ли вы, насколько хорошо он будет масштабироваться? Если вы нажмете 10 потоков, ваше время отклика станет 700 мс или оно останется стабильным на уровне 70 мс?

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

Что касается потребления, вы бы ограничили пул MDB 10 bean-компонентами или чем-то еще, это просто зависит от того, на что способен масштабироваться удаленный сервер и на что способен масштабироваться ваш сервер. Вместо того, чтобы использовать 2 очереди (и это основано только на описанной вами проблеме), я бы использовал только одну. Создайте MDB, которая делает все, что вы делаете сейчас, т.е. захватывает xml и сохраняет его. Наконец, если вы обнаружите, что вам нужно масштабирование, это просто вопрос создания кластера и добавления узлов. Каждый узел будет иметь пул MDB, с которым он работает.

Что касается Asychronous, как вы собираетесь контролировать размер пула и все остальное, что вам дает MDB? Я не говорю, что вы не могли, но кажется, что вы изобретаете велосипед.

person Preston    schedule 21.11.2012
comment
Спасибо за ваш ответ. Удаленный сервер сможет идеально масштабироваться в пределах, до которых я могу масштабироваться. Я реализовал это, используя только одну очередь, содержащую URL-адреса, и MessageDrivenBean, выполняющий всю логику. Однако я получаю: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction в строке em.merge(entity) в моем MessageDrivenBean. Это происходит каждые 50 секунд (inno_db_lock_wait_timeout). Могу ли я одновременно объединиться? Или это нужно отдельно? - person Tim; 23.11.2012
comment
50 секунд за слияние? Это нормально? - person Preston; 23.11.2012
comment
Нет, это занимает доли секунды, полагаю, где-то тупик. На данный момент я обошел это, используя транзакции, управляемые bean-компонентами. Я понял, что проблемы параллелизма относительно сложны и что мое понимание транзакций не является фантастическим, поэтому я собираюсь немного больше прочитать/поработать, а затем посмотреть, смогу ли я это решить. Спасибо. - person Tim; 23.11.2012
comment
Я пошел вперед и разобрался с этим сейчас. Если бы вы могли подтвердить, что это правильный способ достичь того, чего я хотел, это придало бы мне некоторую уверенность. Я использовал Singleton Bean с: @ConcurrencyManagement(ConcurrencyManagementType.CONTAINER) @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) @Lock(LockType.WRITE) для выполнения всех операций слияния (один метод). Кроме того, чтобы обойти ограничение в 1000 сообщений для производителя сообщений, я использовал UserTransaction.begin() и ut.commit() каждые 1000 добавленных сообщений. Все это (кажется) хорошо работает. - person Tim; 26.11.2012
comment
Ваш синглтон не будет масштабироваться, как только вы перейдете к кластеру. Вы пробовали Требовать новую транзакцию в MDB. Без вашего кода трудно понять, что у вас происходит, но звучит ненормально. - person Preston; 26.11.2012
comment
Да, но хотя каждая MDB имеет свою собственную транзакцию, фактическая операция слияния имеет решающее значение в моем коде, поэтому по-прежнему возникают проблемы с параллелизмом, когда другой поток видит устаревшие значения. Мне принципиально нужно где-то пройти через один критический раздел (чтение-обновление-слияние), либо явно, либо через конструкцию JavaEE. Этот критический раздел очень быстрый, поэтому я достиг своей цели по ускорению, он работает чуть более чем в 50 раз быстрее, что ограничено моими сетевыми ресурсами. - person Tim; 26.11.2012

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

person Olivier Liechti    schedule 22.11.2012