Потоки, обрабатывающие пакетное задание в среде сервлетов - PullRequest
2 голосов
/ 17 сентября 2011

У меня есть веб-приложение Spring-MVC, Hibernate, (Postgres 9 db). Пользователь с правами администратора может отправить запрос на обработку около 200 000 записей (каждая запись собирается из разных таблиц с помощью объединений). Такая операция запрашивается еженедельно или ежемесячно (ИЛИ всякий раз, когда данные достигают предела около 200 000/100 000 записей). На конце базы данных я правильно реализую пакетирование.

  • ПРОБЛЕМА: Такой длительный запрос удерживает поток сервера и вызывает страдания обычных пользователей.

  • ТРЕБОВАНИЕ: большое время ответа на этот запрос не является проблемой. То, что требуется, не заставляет других пользователей страдать из-за этого трудоемкого процесса.

  • МОЕ РЕШЕНИЕ:

    Реализация пула потоков с использованием абстракции Spring taskExecutor. Поэтому я могу инициализировать свой пул потоков, скажем, с 5 или 6 потоками и разбить 200 000 записей на более мелкие куски, скажем, размером 1000 каждый. Я могу стоять в очереди в этих кусках. Чтобы в дальнейшем позволить обычным пользователям иметь более быстрый доступ к БД, возможно, я смогу заставить каждый работающий поток спать в течение 2 или 3 секунд. Преимущества этого подхода, который я вижу, заключаются в следующем: вместо того, чтобы выполнять огромный интерактивный запрос БД за один раз, мы имеем асинхронный дизайн, охватывающий большее время. Таким образом, ведет себя как несколько обычных запросов пользователей.

Могут ли некоторые опытные люди высказать свое мнение по этому поводу? Я также читал о реализации того же поведения с помощью промежуточного программного обеспечения, ориентированного на сообщения, такого как JMS / AMQP ИЛИ Quartz Scheduling. Но, честно говоря, я думаю, что внутренне они также сделают то же самое, то есть создадут пул потоков и ставят в очередь на рабочих местах. Так почему бы не использовать Spring taskexecutors вместо добавления совершенно новой инфраструктуры в мое веб-приложение только для этой функции?

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

Ответы [ 2 ]

0 голосов
/ 17 сентября 2011

Huge-db-операции обычно запускаются в часы пик, когда пользовательский трафик значительно меньше.(Скажите что-то вроде 1: 00–2: 00). Как только вы это узнаете, вы можете просто запланировать выполнение задания в это время.Кварц может пригодиться здесь с триггерами, основанными на времени.(Примечание: запуск задания вручную также возможен.)

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

вместо добавления совершенно новой инфраструктуры в мое веб-приложение только для этой функции?

Quartz.jar составляет ~ 350 КБ, и добавление этой зависимости не должно бытьпроблема.Также обратите внимание, что нет причин, по которым это должно быть как веб-приложение.Эти несколько классов, которые выполняют ETL , могут быть помещены в автономный модуль. Запрос из веб-приложения должен быть получен только из таблиц результатов

Все это отдельноЕсли у вас уже была модель master-slave db (обсудите это с вашим dba), то вы могли бы выполнять операции огромный-db с slave-db, а не с master, на что указывали бы обычные пользователи.

0 голосов
/ 17 сентября 2011

Вы можете распараллелить задачи и дождаться завершения всех из них, прежде чем ответить на звонок.Для этого вы хотите использовать ExecutorCompletionService , который доступен в стандарте Java начиная с 5.0

Короче говоря, вы используете локатор службы своего контейнера для создания экземпляра ExecutorCompletionService

ExecutorCompletionService<List<MyResult>> queue = new ExecutorCompletionService<List<MyResult>>(executor);

// do this in a loop
queue.submit(aCallable);

//after looping 
queue.take().get(); //take will block till all threads finish

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

Кварц также имеет механизм планирования заданий, но Java предоставляет стандартный способ.

РЕДАКТИРОВАТЬ: Iвозможно, неправильно понял вопрос.Если вам не нужен более быстрый отклик, а, скорее, вы хотите уменьшить нагрузку на процессор, используйте этот подход

. Вы можете создать внутренний класс, такой как PollingThread, где пакеты, содержащие java.util.UUID для каждого задания и количествоPollingThreads определены во внешнем классе.Это будет продолжаться вечно и может быть настроено так, чтобы ваши процессоры могли свободно обрабатывать другие запросы

 class PollingThread implements Runnable {
            @SuppressWarnings("unchecked")
            public void run(){
                Thread.currentThread().setName("MyPollingThread");
                while (!Thread.interrupted()) {
                    try {
                        synchronized (incomingList) {
                            if (incomingList.size() == 0) {
                                // incoming is empty, wait for some time
                            } else {
                                //clear the original
                                list = (LinkedHashSet<UUID>) 
                                        incomingList.clone();
                                incomingList.clear();
                            }
                        }

                        if (list != null && list.size() > 0) {
                            processJobs(list);
                        }
                        // Sleep for some time
                        try {
                            Thread.sleep(seconds * 1000);
                        } catch (InterruptedException e) {
                            //ignore
                        }
                    } catch (Throwable e) {
                        //ignore                    
                    }
                }
           }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...