Я пытаюсь реализовать механизм A / B-тестирования с использованием актеров Akka (scala 2.12.8
, akka-actor-typed 2.6.1
)
Я разработал его как:
- 1 A / B Testing root actor
- 2 Вариант актеров, потомки A / B Testing actor
Когда актер A / B Testing получит сообщение, он выберет один вариант и переслать сообщение ему.
Однако до сих пор мне не удавалось заставить актера root порождать его детей. Вот мой код.
import akka.actor.typed.{ActorRef, ActorSystem, Behavior}
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
import scala.util.Random
// My variants
object Ranker {
sealed trait Command
final case class Rank() extends Command
def apply(rank: Int): Behavior[Command] = Behaviors.setup { context => new Ranker(context, rank) }
}
class Ranker(context: ActorContext[Ranker.Command], rank: Int) extends AbstractBehavior[Ranker.Command](context) {
override def onMessage(msg: Ranker.Command): Behavior[Ranker.Command] = {
println(rank)
this
}
}
// The root A/B Testing actor
object ABTester {
def apply(): Behavior[Ranker.Command] = Behaviors.setup { context => new ABTester(context) }
}
class ABTester(context: ActorContext[Ranker.Command]) extends AbstractBehavior[Ranker.Command](context) {
val rng = new Random()
// Spawn children actors
val rankers: Seq[ActorRef[Ranker.Command]] = Seq(Ranker(1), Ranker(2)).zipWithIndex
.map { case (b, i) =>
println(s"Spawning ranker $i")
val actorRef = context.spawn(b, s"Ranker $i")
println(s"Spawning ranker $i: Done")
actorRef
}
override def onMessage(msg: Ranker.Command): Behavior[Ranker.Command] = {
rankers(rng.nextInt(rankers.size)) ! msg
this
}
}
// Application entry point
object Main {
def main(args: Array[String]): Unit = {
val actor = ActorSystem(ABTester(), "ABTester")
actor ! Ranker.Rank()
Thread.sleep(5000)
}
}
При выполнении этого примера будет напечатано:
Spawning ranker 0
, но это все, как если бы строка 31 (val actorRef = context.spawn(b, s"Ranker $i")
) никогда не возвращалась ...
Я что-то упустил из-за того, что рождаются детские актеры?