Предложения для простых способов сделать асинхронную обработку в Grails - PullRequest
10 голосов
/ 28 мая 2011

Допустим, у меня есть простой контроллер, подобный этому:

class FooController {

  def index = {
     someVeryLongCompution() //e.g crawl a set of web pages
     render "Long computation was launched."
  }
}

Когда вызывается действие index, я хочу, чтобы метод немедленно возвращался пользователю при асинхронном выполнении длинных вычислений.

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

Я пробовал плагин Executor, но он блокируетhttp-запрос от возврата до завершения длинных вычислений.

Я попробовал подключаемый модуль Quartz, но, похоже, это хорошо для периодических задач (если нет способа запустить задание только один раз?)

Как вы, ребята, обрабатываете такие запросы в Grails?

Ответы [ 8 ]

5 голосов
/ 14 февраля 2012

Где вы хотите обрабатывать veryLongComputation () на том же сервере Grails или на другом сервере?

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

def index = {
     def asyncProcess = new Thread({
          someVeryLongComputation()
     } as Runnable )
     asyncProcess.start()

     render "Long computation was launched."
  }
3 голосов
/ 19 марта 2015

Вы пробовали Grails Promisses API?Это должно быть так просто, как

import static grails.async.Promise
import static grails.async.Promises

class FooController {

  def index = {
     Promise p = Promises.task {
         someVeryLongCompution() //e.g crawl a set of web pages
     }
     render "Long computation was launched."
  }
}
3 голосов
/ 27 ноября 2013

Я знаю, что это очень старый вопрос, просто хотел дать обновленный ответ.

Начиная с Grails 2.3, платформа поддерживает асинхронные вызовы с использованием обработки асинхронных запросов Servlet 3.0 (конечно, необходимо использовать контейнер сервлета 3.0, а в конфигурации должна быть версия сервлета 3.0, что по умолчанию)

Здесь задокументировано: http://grails.org/doc/latest/guide/async.html

В целом, есть два способа добиться того, что вы просили:

import static grails.async.Promises.*
   def index() {
      tasks books: Book.async.list(),
            totalBooks: Book.async.count(),
            otherValue: {
              // do hard work
            }
   }

или способ Servlet Async:

def index() {
    def ctx = startAsync()
    ctx.start {
        new Book(title:"The Stand").save()
        render template:"books", model:[books:Book.list()]
        ctx.complete()
    }
}

Небольшое примечание - метод grails использует обещания, что является значительным (асинхронным) скачком вперед. Любое Обещание может быть приковано к дальнейшему обещанию, отозвано об успехе и неудаче и т. Д.

3 голосов
/ 28 мая 2011

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

1 голос
/ 17 февраля 2014

Если вы хотите использовать плагин Quartz (как мы всегда делаем), вы можете сделать это следующим образом. У нас это хорошо работает:

ОПРЕДЕЛИТЬ ЗАДАНИЕ (без синхронизированных триггеров)

static triggers =  {
    simple name:'simpleTrigger', startDelay:0, repeatInterval: 0, repeatCount: 0
}

CALL .triggerNow () для ручного выполнения задания в асинхронном режиме.

MyJob.triggerNow([key1:value1, key2: value2]); 

Pro Tip # 1

Чтобы получить именованные параметры с другой стороны ...

def execute(context) {
    def value1 = context.mergedJobDataMap.get('key1');
    def value2 = context.mergedJobDataMap.get('key2');
    ...
    if (value1 && value2) {
      // This was called by triggerNow(). We know this because we received the parameters.
    } else {
      // This was called when the application started up. There are no parameters. 
    }
}

Pro Tip # 2

Метод execute всегда вызывается при запуске приложения, но именованные параметры отображаются как нулевые.

Документация: http://grails.org/version/Quartz%20plugin/24#Dynamic%20Jobs%20Scheduling

1 голос
/ 14 февраля 2012

Попробуйте Плагин событий Spring - поддерживает асинхронные прослушиватели событий.

0 голосов
/ 30 мая 2014

Grails 2.2.1 Решение У меня было дополнительное требование, согласно которому отчет должен автоматически открывать окно, когда оно будет завершено.Поэтому я выбрал способ сервлета сверху с изюминкой.Я заменил рендеринг вида строкой в ​​формате json, чтобы это выглядело так.Кроме того, моя клиентская часть - это не представление gsp, а ExtJS 4.1.1 (продукт HTML5)

enter code here
def index() {
    def ctx = startAsync() 
    ctx.start ({

        Map retVar = [reportId: reportId, success: success];
        String jsonString = retVar as JSON;

        log.info("generateTwoDateParamReport before the render out String is: " + jsonString);

        ctx.getOriginalWebRequest().getCurrentResponse().setContentType("text/html");
        ctx.getOriginalWebRequest().getCurrentResponse().setCharacterEncoding("UTF-8");
        log.info("current contentType is: "ctx.getOriginalWebRequest().getCurrentResponse().contentType);
        try {
           ctx.getOriginalWebRequest().getCurrentResponse().getWriter().write(jsonString);
           ctx.getOriginalWebRequest().getCurrentResponse().getWriter().flush();
           ctx.getOriginalWebRequest().getCurrentResponse().setStatus(HttpServletResponse.SC_OK);
        }
        catch (IOException ioe)
        {
            log.error("generateTwoDateParamReport flush data to client failed.");
        }
        ctx.complete();
        log.info("generateNoUserParamsReport after complete");
    });
}
0 голосов
/ 29 мая 2011

Лучшее решение для такого рода проблем - использовать JMS через плагин JMS .

Для более простой реализации, которая не требует внешнего сервера / службы, вы можете попробовать плагин Spring Events .

...