使用Akka在一天中的固定时间安排任务

时间:2012-12-04 10:10:39

标签: scala scheduled-tasks akka

我是Akka的初学者。我需要每天在固定的时间安排一项任务,比如上午8点。我所知道的是定期安排任务,例如

import akka.util.duration._

scheduler.schedule(0 seconds, 10 minutes) {
  doSomething()
}
  

在Akka的一天中的固定时间安排任务的最简单方法是什么?

小括号

使用此功能很容易做我想要的事情。玩具实现看起来像

scheduler.schedule(0 seconds, 24 hours) {
  val now = computeTimeOfDay()
  val delay = desiredTime - now

  scheduler.scheduleOnce(delay) {
    doSomething()
  }
}

这并不难,但我介绍了一点竞争条件。事实上,考虑一下如果我在上午8点之前启动它会发生什么。外部闭包将开始,但到我计算delay时,我们可能会在上午8点之后。这意味着内部关闭 - 应立即执行 - 将推迟到明天,从而跳过执行一天。

有一些方法可以解决这种竞争条件:例如,我可以每12小时执行一次检查,而不是立即安排任务,将其发送给一次不接受多个任务的演员。 / p>

但可能,这已经存在于Akka或某些扩展中。

4 个答案:

答案 0 :(得分:6)

要在Akka中使用这种计划,您必须自己动手或使用Quartz,可以通过Akka Camelprototype quartz for akka

如果你不需要任何花哨和非常准确的东西,那么我只计算第一次所需的延迟,并将其作为计划调用的开始延迟,并信任间隔。

答案 1 :(得分:5)

写一次,每天跑步

val GatherStatisticsPeriod = 24 hours

private[this] val scheduled = new AtomicBoolean(false)

def calcBeforeMidnight: Duration = { 
  // TODO implement 
} 

def preRestart(reason: Throwable, message: Option[Any]) {
  self ! GatherStatisticsScheduled(scheduled.get)
  super.preRestart(reason, message)
}

def schedule(period: Duration, who: ActorRef) = 
  ServerRoot.actorSystem.scheduler
    .scheduleOnce(period)(who ! GatherStatisticsTick)

def receive = {

  case StartServer(nodeName) => 
    sender ! ServerStarted(nodeName)
    if (scheduled.compareAndSet(false, true)) 
      schedule(calcBeforeMidnight, self)

  case GatherStatisticsTick =>
    stats.update
    scheduled.set(true)
    schedule(GatherStatisticsPeriod, self) 

  case GatherStatisticsScheduled(isScheduled) =>
    if (isScheduled && scheduled.compareAndSet(false, isScheduled))
      schedule(calcBeforeMidnight, self)

}

我相信Akka的调度程序会以某种方式内部处理重启。我使用非持久的方式向自己发送消息 - 实际上没有严格的交付保证。此外,滴答可能会有所不同,因此GatherStatisticsPeriod可能是一个函数。

答案 2 :(得分:4)

我们假设您希望每天下午13点执行任务。

import scala.concurrent.duration._
import java.time.LocalTime

val interval = 24.hours
val delay = {
  val time = LocalTime.of(13, 0).toSecondOfDay
  val now = LocalTime.now().toSecondOfDay
  val fullDay = 60 * 60 * 24
  val difference = time - now
  if (difference < 0) {
    fullDay + difference
  } else {
    time - now
  }
}.seconds

system.scheduler.schedule(delay, interval)(doSomething())

另请注意,服务器时区可能与您的不同。

答案 3 :(得分:0)

只需添加另一种实现方法,就可以使用Akka Streams通过ticking一条消息并按时进行过滤。

Source
  .tick(0.seconds, 2.seconds, "hello") // emits "hello" every two seconds
  .filter(_ => {
    val now = LocalDateTime.now.getSecond
    now > 20 && now < 30 // will let through only if the timing is right.
  })
  .runForeach(n => println("final sink received " + n))