1. 序章

Akkaアクターシステムは、タスクの定期的な実行を管理するためのAkkaスケジューラーを提供します。 このチュートリアルでは、 AkkaSchedulerを使用してタスクをスケジュールする方法を説明します。

2. 依存

プロジェクトにAkka-actor依存関係を追加しましょう:

libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % "2.6.8"

3. 単一実行スケジューラ

単一の実行スケジューラにより、タスクの実行を延期することができます。 タスクは、構成された遅延の後に実行されます。

単一の実行スケジューラを作成する方法を見てみましょう。

このチュートリアルでは、次のように単純なアクターを作成できます。

case class Greet(to: String, by: String) 
case class Greeted(msg: String)
class Greetings extends Actor {
  override def receive: Receive = {
    case greet: Greet =>
    sender ! Greeted(s"${greet.by}: Hello, ${greet.to}")
  }
}

また、ActorSystemを初期化する必要があります。

val schedulerActorSystem = ActorSystem("akka-scheduler-system")

その後、GreetingsアクターのActorRefを作成できます。

val greeter = schedulerActorSystem.actorOf(Props(classOf[Greetings]))
val greeting = Greet("Detective","Lucifer")
schedulerActorSystem.scheduler.scheduleOnce(5.seconds, greeter, greeting)

メソッドscheduleOnceは、次の3つのパラメーターを取ります。

  • 期間(遅延)その後、タスクが実行されます
  • アクターにメッセージを送信します
  • 送信するメッセージ

上記の例では、スケジューラーは5秒後にメッセージgreetをアクターgreeterに送信します。 実行のために暗黙のExecutionContextを提供することを忘れないでください。

または、Runnableインターフェイスを使用して上記のタスクを実行することもできます。

run メソッドでタスクの詳細を実装します。このメソッドは、構成された遅延の後に実行されます。

system.scheduler.scheduleOnce(5.seconds, new Runnable {
  override def run(): Unit = greeter ! greet
})

4. 定期的な実行

スケジューラーを使用すると、定期的なタスクを作成して簡単に実行できます。

たとえば、greetメッセージをGreetings アクターに、最初の100ミリ秒の遅延後に1秒に1回送信できます。

system.scheduler.schedule(100.millis, 1.seconds, greeter, greet)

または、 Runnable インターフェイスを使用して、定期的なスケジュールを作成することもできます。

system.scheduler.schedule(10.millis, 250.millis, new Runnable {
  override def run(): Unit = greeter ! greet
})

5. 遅延の種類

Akkaスケジューラは、固定遅延実行と固定レート実行の2種類のスケジューリング遅延を提供します。

5.1. 固定-遅延

固定遅延実行では、後続の実行間の遅延は常に少なくとも指定された間隔値になります。 次の実行時間は、現在の実行時間の実行後にのみ計算されます。 進行中のタスクが長時間実行されている場合、次の実行は遅延します。 その結果、後続の2つの実行間の時間差が一定でない場合があります。

Akkaバージョン2.6から、 scheduleメソッドは非推奨になり、Akkaは代わりにscheduleWithFixedDelayを使用することを提案します

したがって、scheduleWithFixedDelayを使用するように前の例を書き直すことができます。

system.scheduler.scheduleWithFixedDelay(10.millis, 250.millis, greeter, greet)

5.2. 固定金利

固定レートの実行では、進行中のタスクに時間がかかる場合、後続のタスクの遅延が調整されます

たとえば、実行間のスケジュールされた遅延が500ミリ秒で、現在の実行に200ミリ秒かかるとします。 次に、300ミリ秒(500ミリ秒から200ミリ秒)後に次の実行が行われます。

固定レートスケジューラを作成するために、scheduleAtFixedRateメソッドを使用できます。

system.scheduler.scheduleAtFixedRate(10.millis, 500.millis)(new Runnable {
  override def run(): Unit = greeter ! greet
})

6. スケジューラのキャンセル

スケジュールを作成すると、Cancellableインスタンスが返されます。 このインスタンスを使用して、アクティブなスケジューラーをキャンセルできます。

val schedulerInstance:Cancellable = system.scheduler.schedule(
  100.millis, 1.seconds, greeter, greet)
schedulerInstance.cancel()

7. アクタータイマー

スケジューリングがアクター内で行われる場合、AkkaはSchedulerの代わりにアクタータイマーを使用することを提案します。 アクタータイマーは、アクターが再起動したときにスケジュールを処理します。

Scheduler を使用すると、メッセージのライフサイクルを管理するのが難しくなる可能性があります。 akka.actor.Timers 特性を実装することにより、Timerを作成できます。

class TimerActor(replyTo: ActorRef) extends Actor with Timers {
  override def preStart(): Unit = {
    timers.startPeriodicTimer(PeriodicTimerKey, PeriodicTick, 200.millis)
    super.preStart()
  }
}

8. スケジューラの精度

Akkaスケジューラは、メッセージの高スループットを実現するように設計されています。 ただし、それはあまり正確ではありません。 タスクが指定された正確な時間に実行される保証はないため、スケジューラーはスケジュールされた遅延の数ミリ秒後に実行される可能性があります。

また、特定の時間にタスクを実行するために使用することはできません。 このような場合は、 QuartzSchedulerまたはAkkaQuartzSchedulerのいずれかを使用できます。

9. 結論

この記事では、Akkaスケジューラーを使用して将来のいくつかのタスクをスケジュールする方法を見てきました。

このチュートリアルのサンプルコードと関連するテストは、GitHubから入手できます。