繁体   English   中英

如何从坚持石英作业安排 Scala Future

[英]How to schedule scala Future from persist quartz job

假设,有一些 Scala 代码应该在javaquartz 库的帮助下进行调度。 我们需要将这段代码执行的结果存储在作业上下文中,以便在下一次作业执行中访问这个结果。 例如,有一些CounterService有一个应该被调度的inc函数:

trait CounterService {
  def inc(): Int
}

以下石英作业调用inc并将其结果成功存储在JobDataMap

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
class CounterJob extends Job {
  val counterService: CounterService = ...

  override def execute(context: JobExecutionContext): Unit = {

    val newCounterValue: Int = counterService.inc()

    val map = context.getJobDetail.getJobDataMap
    map.put("counter", newCounterValue)  
  }
}

我们可以在其他地方随时获得工作结果(如果我们有参考scheduler ):

val scheduler: Scheduler = ...
// gets details of our CounterJob which was created and registered in the scheduler
// by the name "counter-job" (it is not shown in our example)
val job = scheduler.getJobDetail(JobKey.jobKey("counter-job")) 
// this map will contain the job result which was stored by the key "counter"
val map = job.getJobDataMap.asScala 

但是如果我们想从quartz-job 中执行异步代码,这个方法就不起作用了。 例如,假设我们的柜台服务如下:

trait AsyncCounterService {
  def asyncInc(): Future[Int]
}

我们可以尝试通过以下方式来实现我们的工作。 但它不能正常工作,因为CounterJob.execute方法可以早于asyncCounterService.asyncInc执行。 我们不能在JobDataMap存储asyncInc结果:

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
class CounterJob extends Job {
  val counterService: AsyncCounterService = ...  
  val execContext: ExecutionContext = ...

  override def execute(context: JobExecutionContext): Unit = {

    // # 1: we can not influence on the execution flow of this future 
    //      from job scheduler.
    val counterFuture: Future[Int] = counterService.asyncInc() 

    counterFuture.map { counterValue: Int =>

      val map = context.getJobDetail.getJobDataMap  
      // #2: this action won't have any effect
      map.put("counter", counterValue)              
    }
  }
}

此解决方案中至少有两个问题在上面的代码中标记为#1 ...#2 ...注释。

有没有更好的做法来解决这个问题? 换句话说,如何通过将Future's结果存储在JobDetailData映射中Future从持久性石英作业中安排 scala Future

如果 CounterJob 之后的所有内容都需要有 counterService 值,那么只需阻塞并等待 CounterJob 中的 Future 即可。 无论如何,在那段时间内没有任何东西可以执行,因为尚未计算该值。

import scala.concurrent.{Await,Future}
...

 try {
      val counterValue  = Await.result(counterFuture, 5.seconds)
      map.put("counter", counterValue)       
    } catch {
      case t: TimeoutException => ...
      case t: Exception => ...
   }

如果您在该工作中有多个异步期货,则可以将它们与flatMap, map的一元链、 for comprehension flatMap, map操作 a 或来自Future伴随对象(例如Future.sequence静态辅助方法结合起来,那么最终结果将是一个结合所有异步操作的未来,您可以使用Await

通常,等待期货被认为是一种不好的做法。 因为这会阻止执行程序线程在等待未来完成时执行任何其他操作。

但是,在这里,您将另一个作业调度框架与另一个并发范式混合在一起。 如上所述,在特定示例中,阻塞是可以的,因为后面的一切都依赖于第一次计算。

如果可以同时运行其他作业,则有多种方法可以解决此问题:

  1. 有一种方法可以从工作中返回未来。 然后,您可以等待此未来完成,然后再安排相关作业。
  2. 作业有某种自定义事件侦听器机制,可以从作业中触发。 counterFuture.map {context.notify("computationReady")}
  3. 有特定的 AsyncJob 支持非阻塞 io,它期望 java Future 作为返回。 然后你可以将 Scala Future 转换为 Java Future

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM