关于在Grails中进行异步处理的简单方法的建议

时间:2011-05-28 18:13:41

标签: servlets grails asynchronous quartz-scheduler

假设我有一个像这样的简单控制器:

class FooController {

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

当调用索引操作时,我希望方法在异步运行长计算时立即返回给用户。

我知道最有效的方法是在架构中使用消息代理,但我想知道是否有更简单的方法来执行此操作。

我尝试了Executor插件,但阻止了http请求的返回,直到长计算结束。

我尝试了Quartz插件,但这似乎对周期性任务有好处(除非有办法只运行一次作业?)

你们是如何在Grails中处理这些请求的?

8 个答案:

答案 0 :(得分:5)

您希望在同一个Grails服务器或其他服务器上处理veryLongComputation()?

如果是同一台服务器,则不需要JMS,另一种选择是创建一个新线程并异步处理计算。

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

     render "Long computation was launched."
  }

答案 1 :(得分:3)

如果您在Grails Quartz中使用简单触发器并将repeatCount设置为0,则作业将只运行一次。然而,它与用户请求分开运行,因此您需要找到一些在用户完成时与用户通信的方式。

答案 2 :(得分:3)

我知道这是一个非常古老的问题,只是想给出一个更新的答案。

自grails 2.3以来,该框架支持使用Servlet 3.0异步请求处理的异步调用(当然,必须使用servlet 3.0容器,并且配置中的servlet版本应为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异步方式:

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

小注 - grails方法使用promises,这是一个主要的(异步)跳跃。任何承诺都可以被锁定以进一步承诺,成功回调并失败等等

答案 3 :(得分:3)

您是否尝试过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."
  }
}

答案 4 :(得分:1)

尝试Spring events plugin - 它支持异步事件侦听器。

答案 5 :(得分:1)

如果您想使用Quartz插件(我们一如既往),您可以这样做。它对我们很有用:

定义作业(没有定时触发器)

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

CALL< job> .triggerNow() 以异步模式手动执行作业。

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

专家提示#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. 
    }
}

专家提示#2

执行方法总是在应用程序启动时被调用,但命名参数为null。

文档:http://grails.org/version/Quartz%20plugin/24#Dynamic%20Jobs%20Scheduling

答案 6 :(得分:0)

此类问题的最佳解决方案是通过JMS plugin使用JMS。

对于更简单的实现,不需要外部服务器/服务,您可以尝试Spring Events插件。

答案 7 :(得分:0)

Grails 2.2.1解决方案我有一个额外的要求,即报告必须在完成时自动弹出一个窗口。所以我选择了上面的servlet方式。我用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");
    });
}