「Akka Scheduler 定期実行」とかで検索をかけるとakka-quartz-schedulerやそれをPlay frameworkと組み合わせた例がでてくるのですが、今回はそれらを使わずにAkka Schedulerだけで定期実行したかったのです。

やりたいこと


  • Akka Schedulerそのもので定期実行する
  • akka-quartz-schedulerは使わない
  • Play frameworkも使わない

akka-quartz-schedulerはcron形式で指定した設定ファイルに基づいて処理を定期実行してくれるAkkaの拡張ライブラリです。今回は時間を指定する必要がなく、単純に定期的なスパンで実行できればよかったので使用しませんでした。ただ、実際に業務で定期実行したりする場合は時間指定したいことも多いでしょうし、こちらのライブラリを使うというのでよいのではないかという気がします。

環境


  • akka-actor 2.5.19

実装していく


基本的に公式ドキュメントにサンプルが載っているのですが、よくわからなかった点があったりしたので調べたことを書きながら書いていきます。

まず、依存ライブラリをプロジェクトに記述。

1
libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.5.19"

次にコードですがこんな感じになりました。1秒に1回「test akka scheduler」という出力を垂れ流します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package net.yoshinorin.actortest

import akka.actor.{Actor, ActorSystem, Props}
import scala.concurrent.duration._

object AkkaSchedulerTest extends App {

import actorSystem.dispatcher

val actorSystem = ActorSystem("TestActor")
val testActor = actorSystem.actorOf(Props(new TestActor))
//scheduleは暗黙のパラメータとしてimplicit executor: ExecutionContextを引数としてとります
actorSystem.scheduler.schedule(0 seconds, 1 seconds, testActor, Test)

}

case class Test()

class TestActor extends Actor {

override def receive: Receive = {
case Test => {
println("test akka scheduler")
}
}

}

import actorSystem.dispatcherの部分ですが、これは公式のサンプルではimport system.dispatcherと書かれています。初めはなにをimportしているのか全く分からなかった(これ、サンプルのsystemという命名が解り辛くないですか???)のですが、自分で書いてみるとわかりました。

これはActorSystem("TestActor")で生成したActorSystemインスタンスのdispathcer変数です。

なぜこのimportが必要かというとscheduleが暗黙のパラメータとしてimplicit executor: ExecutionContextを引数としてとるからです。dispatcherはExecutionContextを拡張したExecutionContextExecutorなのでこれを引数としてとることになります。

つまり、暗黙のパラメータを明示的に記述して下記のように書くこともできます。

1
2
3
4
5
6
7
object AkkaSchedulerTest extends App {

val actorSystem = ActorSystem("TestActor")
val testActor = actorSystem.actorOf(Props(new TestActor))
actorSystem.scheduler.schedule(0 seconds, 1 seconds, testActor, Test)(actorSystem.dispatcher)

}

これはどうもFutureと同じっぽいです。FutureもExecutionContextExecutorを引数として要求します。Futureのサンプルとかでimport ExecutionContext.Implicits.globalと書くのは今回のAkka Schedulerのコードでやっていることと基本的な概念は同じだと思います。たぶん。

ちなみに、AkkaではMessageDispatcherがExecutionContextを兼ねます。MessageDispatcherの実装を追っていくとわかりますがはExecutionContextExecutorを継承しています。

実行


あとはこれをsbtとかで実行してみます。すると1秒ごとに「test akka scheduler」という文字列が出力されます。

1
2
3
4
5
6
7
> sbt runMain net.yoshinorin.actortest.AkkaSchedulerTest

test akka scheduler
test akka scheduler
test akka scheduler
test akka scheduler
...

まとめ


またもや日本語の情報がないシリーズで大変困ったのですが、暗黙のパラメータとかExecutionContextのあたりの理解も進んだので良かったと思います。しかし、暗黙のパラメータとかScalaを始めてから100回くらい調べては忘れてを繰り返しているので大変厳しい。日常的に使う状況でないとすぐ忘れてしましますね…。大変厳しい。

追記

akka-quartz-schedulerでもやってみました