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を取得することができました。