夢とガラクタの集積場

落ちこぼれ三流エンジニアである管理人の夢想=『夢』と、潰えた夢=『ガラクタ』の集積場です。

ActorSystemからActorを検索し、メッセージを送信する

こんにちは。

今回は生成したActorに対してメッセージを送信するのではなく、
元々存在しているActorを取得してきてメッセージを送信することを試してみます。

これができるようになれば、元々存在しているActorSystemにアクセスし、
Actorに対してメッセージを送れるようになる、
つまりは複数のActorSystem間でのメッセージ送受信につながるはずです。

1. 1Actor取得確認

というわけで、実際にコードを書いてみます。
まず、下記の定義は継続使用します。
■application.conf

akka.actor.deployment {
  /router1 {
  router = round-robin-pool
  nr-of-instances = 3
  }
}

その上で、下記のコードを記述します。
■ReferenceApp.scala

object ReferenceApp extends App {
  override def main(args: Array[String]): Unit = {
    implicit val system = ActorSystem.apply("ReferenceApp")
    // router1配下に$a、$b、$cのActorが生成される
    val router1 = system.actorOf(FromConfig.props(Props[MessagePrintActor]),
      "router1")

    val actor1 = system.actorSelection("akka://ReferenceApp/user/router1/$b")
    actor1 ! "Message1"

    router1 ! "Message2"
    router1 ! "Message3"

    Thread.sleep(1000)
    system.shutdown()
  }
}

実行すると、下記の出力となります。(返信先が存在しないエラーは省略)

akka://ReferenceApp/user/router1/$b: Received String Message1
akka://ReferenceApp/user/router1/$a: Received String Message2
akka://ReferenceApp/user/router1/$b: Received String Message3

「Message1」はactorSelectionで取得したActorに通知しています。
そのため、router1配下に生成された$bのActorがactorSelectionで取得できたことがわかります。

尚、actorSelection部は下記のコードを記述した場合でも同様に取得することができました。
元々「system」自体が「akka://ReferenceApp」となっているので、
そこからの相対パスでも取得可能なようです。

val actor1 = system.actorSelection("/user/router1/$b")

2. 複数Actor取得確認

フルパスの指定ではなく、パターンでも取得可能なようなので、
下記のコードも試してみました。
■ReferenceApp.scala

object ReferenceApp extends App {
  override def main(args: Array[String]): Unit = {
    implicit val system = ActorSystem.apply("ReferenceApp")
    // router1配下に$a、$b、$cのActorが生成される
    val router1 = system.actorOf(FromConfig.props(Props[MessagePrintActor]),
      "router1")

    val actors = system.actorSelection("/user/router1/*")
    actors ! "Message1"

    router1 ! "Message2"
    router1 ! "Message3"

    Thread.sleep(1000)
    system.shutdown()
  }
}

すると、結果は下記のようになりました。

akka://ReferenceApp/user/router1/$a: Received String Message1
akka://ReferenceApp/user/router1/$b: Received String Message1
akka://ReferenceApp/user/router1/$c: Received String Message1
akka://ReferenceApp/user/router1/$a: Received String Message2
akka://ReferenceApp/user/router1/$b: Received String Message3

actorSelectionメソッドはどうやら複数のActorを取得することができるようですね。
で、取得した結果のオブジェクトに対してメッセージを送信すると、
条件を満たすActor全てに配信されるようです。

実際、取得されるオブジェクトもActorSelection型となっており、
複数のActorに対してメッセージを配分可能な仕組みになっているように見えます。

3. 検索の結果取得されたActorにメッセージを送信した応答を取得する

では、応答のメッセージがどうなっているのか、も確認してみます。
どうやら、以前と同様にInboxを用いることで受信メッセージの取得が可能なようです。
■ReferenceApp.scala

object ReferenceApp extends App {
  override def main(args: Array[String]): Unit = {
    implicit val system = ActorSystem.apply("ReferenceApp")
    // router1配下に$a、$b、$cのActorが生成される
    val router1 = system.actorOf(FromConfig.props(Props[MessagePrintActor]),
      "router1")

    val actors = system.actorSelection("/user/router1/*")
    val rootInbox = ActorDSL.inbox()
    actors.tell("Path1", rootInbox.getRef())
    actors.tell("Path2", rootInbox.getRef())

    // 非同期処理のため、以後のreceiveがPath2の到着前に実行されるのを防止するために待ちを入れる
    Thread.sleep(1000)

    val received1 = rootInbox.receive()
    println("received1:" + received1)
    val received2 = rootInbox.receive()
    println("received2:" + received2)
    val received3 = rootInbox.receive()
    println("received3:" + received3)
    try {
      val received4 = rootInbox.receive()
      println("received4:" + received4)
    }
    catch {
      case ex:TimeoutException => { println("Exception Occured." + ex.getMessage)}
    }

    actors.tell("Path3", rootInbox.getRef())
    val received5 = rootInbox.receive()
    println("received5:" + received5)

    router1 ! "Test1"

    Thread.sleep(1000)
    system.shutdown()
  }
}

結果は下記のようになります。

akka://ReferenceApp/user/router1/$a: Received String Path1
akka://ReferenceApp/user/router1/$c: Received String Path1
akka://ReferenceApp/user/router1/$a: Received String Path2
akka://ReferenceApp/user/router1/$b: Received String Path1
akka://ReferenceApp/user/router1/$c: Received String Path2
akka://ReferenceApp/user/router1/$b: Received String Path2
[WARN] [08/16/2014 14:38:39.077] [ReferenceApp-akka.actor.default-dispatcher-5] [akka://ReferenceApp/system/dsl/inbox-1] dropping message: either your program is buggy or you might want to increase akka.actor.dsl.inbox-size, current value is 3
received1:akka://ReferenceApp/user/router1/$a: Received String Path1
received2:akka://ReferenceApp/user/router1/$c: Received String Path1
received3:akka://ReferenceApp/user/router1/$a: Received String Path2
Exception Occured.deadline passed
akka://ReferenceApp/user/router1/$a: Received String Path3
akka://ReferenceApp/user/router1/$b: Received String Path3
akka://ReferenceApp/user/router1/$c: Received String Path3
received5:akka://ReferenceApp/user/router1/$a: Received String Path3
akka://ReferenceApp/user/router1/$a: Received String Test1

Inboxが各々のActor($a、$b、$c)の応答をそれぞれ受信していることがわかります。
また、selectionで取得した場合は$a、$b、$cに対してメッセージが各々配信されますが、
メッセージに対する応答は$a、$b、$cで別のものとして受信することもわかります。

・・と、とりあえずActorSystemからActorを取得することができました。