假设我有一个像这样的简单控制器:
class FooController {
def index = {
someVeryLongCompution() //e.g crawl a set of web pages
render "Long computation was launched."
}
}
当调用索引操作时,我希望方法在异步运行长计算时立即返回给用户。
我知道最有效的方法是在架构中使用消息代理,但我想知道是否有更简单的方法来执行此操作。
我尝试了Executor插件,但阻止了http请求的返回,直到长计算结束。
我尝试了Quartz插件,但这似乎对周期性任务有好处(除非有办法只运行一次作业?)
你们是如何在Grails中处理这些请求的?
答案 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");
});
}