Решение проблемы длительного выполнения запроса в веб-приложении (асинхронный запрос)
вот в чем проблема
пользователь корпоративного веб-приложения выполняет задачу, которая приводит к длинному (очень длинному) запросу базы данных (или другой длительной обработке интенсивной задачи)
проблемы:
- тайм-аут запроса-через некоторое время пользователь может получить тайм-аут запроса
- тайм-аут сеанса-если не используются методы сохранения сеанса, тайм-аут сеанса может произойти
- запрос-нить замок
- С просьбой поток не возвращает, он может блокировать новые requrests (если достигает предела бассейн)
- на некоторых серверах приложений состояние работоспособности сервера может вызвать принудительный перезапуск узла или приложения (из-за длительного потока запросов)
- если пользователь покидает страницу:
- транзакция не отменяется - в результате бесполезной обработки никто не выиграет от
- пользователь не может вернуться, чтобы увидеть результаты после их завершения
- нет индикации прогресса-пользователь просто ждет обновления страницы
есть несколько решений, которые я придумал, но я не уверен, что знаю, что лучше (во всех аспектах, peformance, лучшая практика, элегантность и ремонтопригодность), и я хотел бы знать, что ваше рекомендуемое решение, и если есть решение, которое я пропустил? (да наверное и многие)
плохое решение: используйте поток запроса в качестве рабочий поток, сохранить состояние выполнения в сеансе, проверить состояние вызова AJAX (в сеансе) в другом запросе paralel
компромиссное решение: создайте свой собственный пул потоков, обработайте поток мониторинга, рабочий поток и позаботьтесь о кластеризации путем синхронизации состояний в распределенном транзакционном кэше или постоянном хранилище. это освобождает запрос, но создает потоки, о которых сервер приложений не знает и не закрывается неразвертывания. Его до вас, чтобы отключить потоки в чистом виде, и всегда есть шанс, что вы в конечном итоге утечка что-то. Это также не способ J2EE сделать это.
решение по J2EE: используйте JMS для асинхронной задачи, это то, что это ment для
весной решением: использовать Spring партии
Что бы вы сделали / делали в своих проектах? Какие еще решения вы знаете? кто из тех, кого я отметил выше, является победителем в Ваше мнение?
4 ответов
Я бы сделал комбинацию вашего так называемого " плохого решения "и" решения j2ee":
- исходный поток пользовательского интерфейса отправляет асинхронное сообщение JMS в "бэкэнд" и возвращает.
- серверная часть получает асинхронный запрос и обрабатывает его.
- когда результат (или ошибка) достигается в бэкэнде, он возвращается на уровень контроллера.
- пользовательский интерфейс, все еще выполняющий опрос AJAX или использующий Bayeux / Cometed, получает и отображает результат.
трюк состоит в том, чтобы соответствовать паре запрос / ответ. Я бы сделал это так:
- создайте "AsyncManagerService" (AMS), который имеет сеанс, возможно, даже широкую область приложения, чтобы все потоки говорили с одним экземпляром.
- AMS содержит карту с идентификатором как ключ и любой объект как значение.
- при создании сообщения запроса JMS создайте уникальный случайный ключ и поместите его в jmsCorrelationId сообщения, а также в карту, с
NULL
как ценность. Также передайте этот идентификатор в пользовательский интерфейс. - пусть поток пользовательского интерфейса опросит AMS с ранее сгенерированным id пока значение в карте
NULL
. - когда результат будет готов, пусть ваш приемник JMS поместит его в карту AMS по заданному идентификатору.
- в следующий раз, когда поток пользовательского интерфейса опросит карту, он получит ответ и остановит опрос.
это чисто и хорошо абстрагировано от любого конкретный домен. Чисто техническое решение.
даже если вам не нравится опрос, HTTP не имеет состояния по дизайну, и я думаю, что таким образом опрос происходит только через определенные интервалы.
во всяком случае, я реализовал систему именно с этим шаблоном, и она работает нормально...
решение, которое я использовал раньше, включает Причал Принятый В Cometd вместо "Аякса". Основное различие между Ajax и Cometd заключается в том, что Cometd может использовать больше модели pub/sub - клиент (веб-браузер в этом случае) подчиняется издателю (вашему приложению), и приложение выталкивает обновления и уведомления в веб-браузер, как это предусмотрено моделью ajax постоянного опроса сервера веб-браузером. Вы можете использовать решение Cometd, даже если вы не используете причал - вы можете бросьте jetty jars в папку lib вашего соответствующего веб-сервера, и вам должно быть хорошо идти.
показать сообщение пользователю ,что "ваш запрос принят и займет час, чтобы получить обновление"
вы создаете таблицу, в которой хранятся все эти транзакции и обрабатываете их в пакете на сервере.
пользователю не придется долго ждать и он будет рад увидеть сообщение. Вы можете отправить подтверждение по электронной почте после обработки транзакции.
Это лучшее решение, которое я думаю.
Как долго выполняются эти запросы?
Если вы говорите о сеансах, истекающих, возможно, лучше для пользователя не ждать его. Это всегда будет раздражать пользователя, чтобы ждать 20 минут, достигающих максимума так часто на вкладке этого запроса.
Итак, если действительно длинные запросы имеют место, возможно, лучше всего изменить подход к пользовательскому интерфейсу и "заказать" запрос, который он (она) позже возвращается к просмотру или даже уведомляется по почте, когда он готов.
в таком случае я бы подготовил запрос в БД и кэшировал его в отдельной таблице. Это означает, что у меня будет работа веб-сервера один раз, чтобы зарегистрировать запрос, иметь задачу БД или отдельный процесс/сервер, подготавливающий запросы по запросам и уведомляющий пользователя, а затем веб-сервер отображает их, когда пользователь возвращается за результатами.