2

У меня есть удаленный актер, Bar и местный актер, Foo. Я хочу использовать Foo для передачи сообщений Bar при каждом вызове CLI.Как отправлять сообщения удаленному игроку через CLI с удаленным Akka?

Bar может быть успешно передано сообщений, но Foo висит во время ожидания сообщения. Чтобы исправить это, я добавил sys.exit(0) в конце Foo. Это вызывает проблему ассоциации с системой Foo.

Как я могу закрыть своего местного участника между последовательными выпусками CLI, не убивая моего местного актера вручную?

Заткнись и дай мне код!


Foo:

build.sbt

name := "Foo" 

version := "1.0" 

scalaVersion := "2.11.8" 

libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.4.11" 
libraryDependencies += "com.typesafe.akka" %% "akka-remote" % "2.4.11" 
libraryDependencies += "com.github.scopt" %% "scopt" % "3.5.0" 

fork in run := true 

Main.scala

import akka.actor._ 
import com.typesafe.config.ConfigFactory 

case class Config(mode: String = "", greeting: String="") 

class Foo extends Actor { 
    // create the remote actor 
    val BarActor = context.actorSelection("akka.tcp://[email protected]:2552/user/BarActor") 

    def receive = { 
    case method: String => BarActor ! method 
    } 
} 

object CommandLineInterface { 

    val config = ConfigFactory.load() 
    val system = ActorSystem("FooSystem", config.getConfig("FooApp")) 

    val FooActor = system.actorOf(Props[Foo], name = "FooActor") 

    val parser = new scopt.OptionParser[Config]("Foo") { 
    head("foo", "1.x") 

    help("help").text("prints usage text") 

    opt[String]('m', "method").action((x, c) => 
     c.copy(greeting = x)).text("Bar will greet with <method>") 
    } 
} 

object Main extends App { 
    import CommandLineInterface.{parser, FooActor} 

    parser.parse(args, Config()) match { 
    case Some(config) => FooActor ! config.greeting 
    case None => sys.error("Bad news...") 
    } 
    /* 
    When sys.exit(0) commented, this hangs and Bar greet. 
    When sys.exit(0) uncommented, this doesn't hang, but also Bar doesn't greet. 
    */ 

    //sys.exit(0) 
} 

application.conf

FooApp { 
    akka { 
    loglevel = "INFO" 
    actor { 
     provider = "akka.remote.RemoteActorRefProvider" 
    } 
    remote { 
     enabled-transports = ["akka.remote.netty.tcp"] 
     netty.tcp { 
     hostname = "127.0.0.1" 
     port = 0 
     } 
     log-sent-messages = on 
     log-received-messages = on 
    } 
    } 
} 

Бар:

build.sbt

name := "Bar" 

version := "1.0" 

scalaVersion := "2.11.8" 

libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.4.11" 
libraryDependencies += "com.typesafe.akka" %% "akka-remote" % "2.4.11" 

Main.scala

import akka.actor._ 
import com.typesafe.config.ConfigFactory 

class Bar extends Actor { 
    def receive = { 
    case greeting: String => Bar.greet(greeting) 
    } 
} 

object Bar { 
    val config = ConfigFactory.load() 
    val system = ActorSystem("BarSystem", config.getConfig("BarApp")) 
    val BarActor = system.actorOf(Props[Bar], name = "BarActor") 

    def greet(greeting: String) = println(greeting) 

    def main(args: Array[String]): Unit = { 
    /* Intentionally empty */ 
    } 
} 

application.conf

BarApp { 
    akka { 
    loglevel = "INFO" 
    actor { 
     provider = remote 
    } 
    remote { 
     enabled-transports = ["akka.remote.netty.tcp"] 
     netty.tcp { 
     hostname = "127.0.0.1" 
     port = 2552 
     } 
     log-sent-messages = on 
     log-received-messages = on 
    } 
    } 
} 

Run Foo с sbt 'run-main Main -m hello' и запустить Bar с sbt 'run-main Main'.

Извините за длинный код, но это MVCE для моей проблемы.

Как я могу достичь желаемого поведения - участник CLI умирает между последовательными вызовами CLI с удаленным игроком, ожидающим новых сообщений.

+1

Почему вы думаете, что 'Bar' умер? Есть ли что-то в журнале, указывающем это? –

+0

@ PawełBartkiewicz Я попытался прояснить свой смысл. Извините за промахи. :) Надеюсь, это более понятно. – erip

ответ

3

Это происходит потому, что вы называете sys.exit(0) сразу после отправки сообщения на FooActor, так что есть значительная вероятность того, что выход из приложения перед FooActor получает возможность даже прочитать сообщение, не говоря уже о его вперед BarActor.

Там, кажется, many possible solutions, причем один из них:

class Foo extends Actor { 
    // create the remote actor 
    val BarActor = context.actorSelection("akka.tcp://[email protected]:2552/user/BarActor") 

    override def receive = { 
    case method: String => { 
     BarActor ! method 
     self ! PoisonPill 
    } 
    } 

    override def postStop = { 
    context.system.terminate 
    } 
} 

К сожалению, получается, что система все еще получает закрыли перед отправкой сообщения в Bar.

Я не нашел разумного решения этой проблемы, если вы хотите отправить сообщение в стиле «огонь и забыть». Тем не менее, в большинстве случаев, это желательно, чтобы получить какой-то ответ от удаленного актера, так что вы можете сделать:

class Foo extends Actor { 
    // create the remote actor 
    val BarActor = context.actorSelection("akka.tcp://[email protected]:2552/user/BarActor") 

    override def receive = { 
    case method: String => { 
     BarActor ! method 
     context.become(waitingToKillMyself) 
    } 
    } 

    def waitingToKillMyself: Receive = { 
    case response: String => { 
     println(response) 
     self ! PoisonPill 
    } 
    } 

    override def postStop = { 
    context.system.terminate 
    } 
} 

// ... 

object Main extends App { 
    import CommandLineInterface.{parser, FooActor, system} 
    import system.dispatcher 

    parser.parse(args, Config()) match { 
    case Some(config) => { 
     FooActor ! config.greeting 
     system.scheduler.scheduleOnce(10.seconds, FooActor, PoisonPill) 
    } 

    case None => sys.error("Bad news...") 
    } 
} 

Бар:

class Bar extends Actor { 
    def receive = { 
    case greeting: String => { 
     Bar.greet(greeting) 
     sender() ! "OK" 
    } 
    } 
} 
+0

Это похоже на прочное решение, если это разовое сообщение, но я хочу, чтобы «Бар» в основном всегда ждал сообщения. Будет ли это достигнуто? – erip

+1

Не является ли 'BarSystem' отдельной актерской системой? –

+0

К сожалению, после редактирования не было комментариев. Да, я считаю, что это должно работать нормально (я не могу попробовать это прямо сейчас, я могу сделать это позже, если вы захотите). Поскольку 'FooSystem' и' BarSystem' являются отдельными системами актеров, 'shutdown' или' terminate' должны останавливать только 'FooSystem' ([документация] (http://doc.akka.io/api/akka/2.4/index.html) #[email protected](): scala.concurrent.Future [akka.actor.Terminated])). –

Смежные вопросы