Futures
をgrokして、akkaでパターンを尋ねようとしています。
それで、私は2人の俳優を作り、1人は別の俳優にメッセージを送り返すように頼みます。さて、akkaのFutures
ドキュメントによると、俳優はメッセージを尋ねる(?
)する必要があり、それは彼にFuture
インスタンスを与えるでしょう。次に、アクターは(Await
を使用して)ブロックしてFuture
の結果を取得する必要があります。
まあ、私は自分の将来を成し遂げることは決してありません。何故ですか?
コードは次のとおりです。
package head_thrash
import akka.actor._
import akka.util.Timeout
import scala.concurrent.Await
import scala.concurrent.duration._
object Main extends App {
val system = ActorSystem("actors")
val actor1 = system.actorOf(Props[MyActor], "node_1")
val actor2 = system.actorOf(Props[MyActor], "node_2")
actor2 ! "ping_other"
system.awaitTermination()
Console.println("Bye!")
}
class MyActor extends Actor with ActorLogging {
import akka.pattern.ask
implicit val timeout = Timeout(100.days)
def receive = {
case "ping_other" => {
val selection = context.actorSelection("../node_1")
log.info("Sending ping to node_1")
val result = Await.result(selection ? "ping", Duration.Inf) // <-- Blocks here forever!
log.info("Got result " + result)
}
case "ping" => {
log.info("Sending back pong!")
sender ! "pong"
}
}
}
Duration.Inf
を5.seconds
に変更すると、アクターは5秒間待機し、自分の将来がタイムアウト(TimeoutException
をスローすることにより)であることを通知し、最後に他のアクターが必要なメッセージで返信します。したがって、非同期は発生しません。どうして? :-(
そのパターンを適切に実装するにはどうすればよいですか?ありがとう。
公式のAkka documentation は、Await.resultによって現在のスレッドがブロックされ、アクターがその応答でFutureを「完了する」のを待つと述べています。
あなたのコードがそこで永遠にブロックするのは奇妙です、あなたはあなたのすべてのアプリケーションのためにただ一つのスレッドを持っていますか?
とにかく、コーディングのより「慣用的な」方法は、将来の成功時にコールバックを使用することだと思います。
def receive = {
case "ping_other" => {
val selection = context.actorSelection("../node_1")
log.info("Sending ping to node_1")
val future: Future[String] = ask(selection, "ping").mapTo[String]
future.onSuccess {
case result : String ⇒ log.info("Got result " + result)
}
}
...
それが機能しない2つの理由。
まず、「node_1」はそれ自体を要求し、「ping」は要求の待機をブロックしているため、処理されません。
また、相対パス( "../node_1")のactorSelectionには欠点があります。メッセージパッシングで処理され、アクターがブロックしているため、他のメッセージを処理できません。これは、今後の2.3バージョンのAkkaで改善されましたが、とにかくブロックしないようにする必要があります。