scala 使用 Akka 在一天中的固定时间安排任务
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13700452/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Scheduling a task at a fixed time of the day with Akka
提问by Andrea
I am a beginner with Akka. I need to schedule a task each day at a fixed time of the day, say 8AM. What I know how to do is scheduling a task periodically, for instance
我是 Akka 的初学者。我需要在每天的固定时间安排一项任务,比如早上 8 点。例如,我知道如何定期安排任务
import akka.util.duration._
scheduler.schedule(0 seconds, 10 minutes) {
doSomething()
}
What is the simplest way to schedule tasks at fixed times of the day in Akka?
在 Akka 中在一天的固定时间安排任务的最简单方法是什么?
A small parenthesis
一个小括号
It is easy to do what I want just using this feature. A toy implementation would look like
只需使用此功能即可轻松完成我想做的事情。一个玩具实现看起来像
scheduler.schedule(0 seconds, 24 hours) {
val now = computeTimeOfDay()
val delay = desiredTime - now
scheduler.scheduleOnce(delay) {
doSomething()
}
}
It is not difficult, but I introduced a little race condition. In fact, consider what happens if I launch this just before 8AM. The external closure will start, but by the time I compute delaywe may be after 8AM. This means that the internal closure - which should execute right away - will be postponed to tomorrow, thereby skipping execution for one day.
这并不难,但我引入了一点竞争条件。事实上,考虑一下如果我在早上 8 点之前启动它会发生什么。外部关闭将开始,但当我计算时,delay我们可能在上午 8 点之后。这意味着应该立即执行的内部关闭将推迟到明天,从而跳过一天的执行。
There are ways to fix this race condition: for instance I could perform the check every 12 hours, and instead of scheduling the task right away, sending it to an actor that will not accept more than one task at a time.
有一些方法可以解决这种竞争条件:例如,我可以每 12 小时执行一次检查,而不是立即安排任务,而是将其发送给一次不会接受多个任务的参与者。
But probably, this already exist in Akka or some extension.
但很可能,这已经存在于 Akka 或某些扩展中。
采纳答案by idonnie
Write once, run everyday
写一次,天天跑
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)
}
I believe that Akka's scheduler handles restarts internally, one way or another. I used non-persistent way of sending a message to self - actually no strict guarantee of delivery. Also, ticks may vary, so GatherStatisticsPeriod might be a function.
我相信 Akka 的调度程序会以一种或另一种方式在内部处理重启。我使用非持久方式向自己发送消息 - 实际上没有严格的交付保证。此外,刻度可能会有所不同,因此 GatherStatisticsPeriod 可能是一个函数。
回答by Bj?rn Antonsson
To use this kind of scheduling in Akka, you would have to roll your own or maybe use Quartz, either through Akka Camelor this prototype quartz for akka.
要在 Akka 中使用这种调度,你必须自己滚动或者使用 Quartz,通过Akka Camel或这个原型quartz for akka。
If you don't need anything fancy and extremely accurate, then I would just calculate the delay to the desired first time and use that as the start delay to the schedule call, and trust the interval.
如果您不需要任何花哨且极其准确的东西,那么我只会计算所需的第一次延迟,并将其用作计划调用的开始延迟,并信任该间隔。
回答by Pavel Khamutou
Let's say you want to run your task every day at 13 pm.
假设您希望每天下午 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())
Also remember that server timezone may be different from yours.
另请记住,服务器时区可能与您的时区不同。
回答by ticofab
Just to add another way to achieve it, this can be done using Akka Streamsby tickinga message and filtering on time.
只是添加另一种方法来实现它,这可以通过使用Akka Streams来完成,方法是勾选消息并按时过滤。
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))

