【问题标题】:Error on async job异步作业出错
【发布时间】:2014-09-16 20:53:56
【问题描述】:

我正在尝试创建一个不会阻止请求的异步任务。用户发出请求,任务将启动,控制器将呈现“作业正在运行...”,这是为了避免请求被阻塞等待任务完成。 任务完成后,它将执行 onComplete 并对该任务的结果执行某些操作(例如调用将向用户发送邮件的服务)

| Error 2014-09-16 17:38:56,721 [Actor Thread 3] ERROR gpars.LoggingPoolFactory  - Async execution error: null

代码如下:

package testasync

import static grails.async.Promises.*

class TestController {

  def index() {
    //Create the job
    def job1 = task {
        println 'Waiting 10 seconds'
        Thread.sleep(10000)
        return 'Im done'
    }
    //On error
    job1.onError { Throwable err ->
        println "An error occured ${err.message}"
    }
    //On success
    job1.onComplete { result ->
        println "Promise returned $result"
    }
    render 'Job is running...'
  }

完整的堆栈跟踪:

| Error 2014-09-17 10:35:24,522 [Actor Thread 3] ERROR gpars.LoggingPoolFactory  -  Async execution error: null
Message: null
   Line | Method
 ->>   72 | doCall    in org.grails.async.factory.gpars.GparsPromise$_onError_closure2
  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  |     62 | run       in groovyx.gpars.dataflow.DataCallback$1
  |   1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
  |    615 | run       in java.util.concurrent.ThreadPoolExecutor$Worker
  ^    745 | run . . . in java.lang.Thread

【问题讨论】:

  • 有完整的踪迹吗?
  • 添加了完整的堆栈跟踪
  • 我在 grails 3.2.11 上尝试了你的代码,它可以完美运行而不会出错,除非你调用你的 serviceMethod() 并使用域,你可能需要在你的服务中使用 Domain.withTransaction()代码
  • def yourServiceMethod(){ YourDomain.withTransaction{ YourDomain.column = "new_value" YourDomain.save(flush:true) } }

标签: grails groovy


【解决方案1】:

我结束了使用带有 grails-executor 插件的 executor 框架。我在这里上传了一个非常基本的例子:https://github.com/agusl88/grails-async-job-queuqe

该代码正在使用 grails-executor 插件的“自定义”版本,我从插件 repo 中合并了一些 PR,并打包为 jar,仅用于测试 propuses。插件的repo是这样的:https://github.com/basejump/grails-executor

【讨论】:

    【解决方案2】:

    通过删除 onCompleteonError 调用,我能够在控制器中消除此异常。我猜这个异常的发生是因为当你调用render时父线程结束了。

    所以你的:

    Promise p = task {
        complexAsyncMethodCall(); // (1) do stuff
    }
    .onComplete { result -> println result } // (2) on success
    .onError { Throwable t -> System.err.println("Error: " + t) } // (3) on error
    

    变成:

    Promise p = task {
        try {
            def result = complexAsyncMethodCall(); // (1) do stuff
            println result // (2) on success
        } catch(Throwable t) {
            System.err.println("Error: " + t) // (3) on error
        }
    }
    

    这增加了您的工作 (1) 和结果处理 (2 和 3) 之间的耦合,但您可以通过编写自己的 Closure 包装器来克服这个问题,该包装器将额外的闭包作为参数。像这样的:

    // may not work! written off the top of my head
    class ProcessableClosure<V> extends Closure<V> {
        Closure<V> work;
        Closure<?> onError;
        Closure<?> onComplete;
    
        @Override
        public V call(Object... args) {
            try {
                def result = work.call(args); // (1) do stuff
                onComplete.call(result); // (2) on complete
            } catch(Exception e) {
                onError.call(result); // (3) on error
            }
        }
    }
    

    这使您的代码更具可读性:

    Closure doWork = { complexAsyncMethodCall(); } // (1) do stuff
    Closure printResult = { println it } // (2) on complete
    Closure logError = { Throwable t -> log.error t } // (3) on error
    Closure runEverythingNicely = new ProcessableClosure(work: doWork, onComplete: printResult, onError: logError)
    Promise p = task { runEverythingNicely }
    

    【讨论】:

      【解决方案3】:

      在控制器中创建 Promise 异步任务时,您实际上必须通过在任务上调用 get() 方法来返回响应,否则永远不会调用 onErroronComplete 方法。添加:

      job1.get()
      

      在您致电 render 解决问题之前。

      【讨论】:

      • 但是 get() 阻塞了当前请求,所以直到异步任务完成才执行渲染。我想开始任务,结束请求(避免在用户端等待时间),当任务结束时,用 onComplete 选择结果。
      • @agusluc 你有什么解决办法吗?我在使用 grails 2.4.4 时遇到了同样的问题
      • @ShashankAgrawal 我用我找到的解决方案添加了回复。希望对你有帮助
      • 谢谢@agusluc。我会试试你的解决方案。
      【解决方案4】:

      在我的情况下,只返回一个承诺就可以了。

      MyService.groovy

      import static grails.async.Promises.*
      def getAsync(){
          Promise p = task {
          //Long running task
                  println 'John doe started digging a hole here.'
                  Thread.sleep(2000)
                  println 'John doe working......'
                  return 'Kudos John Doe!'
              }
              p.onError { Throwable err ->
                  println "Poor John"+err
              }
              p.onComplete { result ->
                  println "Congrats." +result
              }
      
              println 'John Doe is doing something here.'
              return p
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-16
        • 2012-08-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多