Архитектура приложения с отложенными задачами на JAVA?
Всем привет! Есть вопрос, так сказать нужен best practices по решению проблемы в разрезе Java приложения.
Есть сайт, на нем пользователь может инициировать медленные операции: например, одна из таких задач могла бы быть — скачивание и парсинг файла 3Gb.
Я подразумеваю, что у меня в проекте будет какой-то набор команд, которые при инициировании я буду класть в сервер очередей, например RabbitMQ и какой-нибудь процесс (далее Демон) приложения будут эту очередь ассинхронно в фоне разбирать. Хочется, чтобы приложение масштабировалось, те, чтобы я мог запустить N Демонов на M серверов и таким образом достиг бы масштабирования обработки. Так же у проекта будет API. UI интерфейс пойдет отдельным проектом и будет ставить задачи через api, так что тут проблем нет — кодовая база у него другая по сути и пересекается долько в моделях предметной области.
В итоге вопрос в следующем: можно ли, чтобы API и Демоны использовали одну кодовую базу и работали более-менее изолировано, чтобы, если захотелось поднять еще одного демона, не пришлось поднимать еще один инстанс API вместе с ним и т.д… Если так не делают я пойму ;) А вообще так-то были мысли запустить простое MVC для API и в отдельном потоке Демона(ов), но не знаю насколько это кашерно.
Решали похожу задачу. Используем ActiveMQ в качестве очереди. Есть N демонов и M API, код общий, но разные Spring конфиги. Сответственно, демон запускает свой конфиг со своими сервисами, API — свой. Но это решение не очень нравится, я планирую использовать модули Maven и вынести общий код в отдельный модуль, и прописать его как зависимость в демон и API часть. Основной минус общего кода — при изменении только API приходится рестартовать сервисы, и наоборот.
Развертывание следует делать как можно более простое — одинаковые конфиги либо централизованная конфигурация. Плюс у нас активно используется JMX для точной настройки отдельных нод.
Можно, а что мешает им это делать? Напишите какую-нибудь обёртку над выбранной вами очередью, которая будет уметь класть новое задание в очередь, брать задание из очереди (pull-ить или через pub-sub), сохранять результат обработанного задания, — и используйте её везде. Интерфейс, кладущий задание в очередь можно научить работать как с самой очередью, так и через api (чтоб использовать тот-же код и в UI). При масштабировании до разумных пределов одного сервера очередей вам скорее всего хватит. Тут скорее вопрос, насколько большой у вас результат обработчиков, нужно ли его где-то хранить (если нужно, то хранилище придется тоже как-то масштабировать).
Пиши в одной кодовой базе, но сделай флаг в конфигурации — отключить работу АПИ, и запускай себе сколько хочешь дополнительных демонов. Я постоянно так делаю, можно в одной кодовой базе содержать несколько функциональных модулей проекта, которые включаются/отключаются опционально.
Поскольку речь всё равно идет об серверах очередей — я бы сделал так, чтобы клиенты подключались к серверу с помощью JMX или любого другого протокола, который поддерживается используемой вами MQ (кстати, почему не ActiveMQ, который прекрасно встравивается в Java приложения?) и говорил «Я клиент, дайте мне задание тоже». Таким образом поднятие клиентов стало бы вопросом тривиальным — запустили Java-приложение, оно само подняло MQ клиента и само начало обрабатывать задания. Остановили/вырубили => соединение отвалилось, заданий туда больше не слать. Если результат выполнения данного задания не вернулся — поставить в очереди первым и отдать первому освободившемуся воркеру.
Если хочется обойтись базовыми явовскими технологиями, то можно сделать диспетчер заданий простым ява-классом и коннектиться к нему через RMI.
Диспетчер может выдавать задания через интерфейс Runnable, а все что делает клиент — это получает очередное задание и запускает run(). Главное, что бы у исполнителя в classpath были необходимые классы с заданиями.