排序Akka路由器消息

时间:2018-10-19 21:55:38

标签: scala akka router

使用akka路由器,我要执行两个不同的操作。但是当我传递消息时,它们是重叠的。这是我的代码。

class Master extends Actor {

    import context._

    val numRoutees = 3
    val router: ActorRef = actorOf (RoundRobinPool (numRoutees).props(Props[Worker]), "router")

    // broadcasts GetString() and receives a random string from each routee
    def stringMessages(replies: Set[String] = Set()): Receive = {
        case GetString() =>
            router ! Broadcast(GetString())     // g
        case reply: String =>
            val updatedReplies = replies + reply
            if (updatedReplies.size == numRoutees) {
                println("result = " + updatedReplies.mkString("[", ",", "]"))
            }
            become(stringMessages(updatedReplies))

        case GetInteger() =>
            become(intMessages())
            //    self ! createArray()  // h      // <- uncommenting this results in an infinte loop

        case _ => println("stringMessages: no matches")
    }

    // broadcasts GetInteger and receives a random integer from each routee
    def intMessages(ints: Set[Int] = Set()): Receive = {
        case GetInteger() =>
            router ! Broadcast(GetInteger())    // e
        case n: Int =>
            val updatedInts = ints + n
            if (updatedInts.size == numRoutees) {
                println("result = " + updatedInts.mkString("[", ",", "]"))
            }
            become(intMessages(updatedInts))

        case GetString() =>
            become(stringMessages())
            self ! GetString()                  // f

        case _ => println("intMessages: no matches")
    }

    override def receive: Receive =
    {
        case GetString() =>
            become(stringMessages())
            self ! GetString()      // c
        case GetInteger() =>
            become(intMessages())
            self ! GetInteger()     // d
        case _ => println("root doesn't match")
    }
}


object MasterTest extends App {
    val system = ActorSystem ("ActorSystem")
    val actor = system.actorOf(Props[Master], "root")

    actor ! GetInteger()        // a
    actor ! GetString()         // b
}

我了解到一些调试语句的执行顺序可能是a -> b -> f -> g。 (请注意代码中注释的语句的ID)。该代码不执行我期望的操作。输出是

result = [a,b,c]

如何使它们按a -> d-> e -> b -> f -> g的顺序执行。如果我添加一个Thread.sleep之类的

actor ! GetInteger()        // a
Thread.sleep(3000)
actor ! GetString()         // b

我得到了预期的输出,即

result = [0,4,6]    // random integers
result = [a,b,c]    // random strings

如何使actor消息排队,以使新消息仅在完全执行前一个消息之后才执行。有什么更好的方式来实现我在become()中所做的工作?如果我想在代码中添加更多状态(例如此处的GetIntegerGetString),则代码变得太多余了,无法使用become(newState)将状态更改为另一状态。

如果我取消注释h,代码也会变成a -> b -> f -> d -> h -> f -> h -> f -> ...的无限循环。因此,我首先了解到这不是正确的实现。

1 个答案:

答案 0 :(得分:1)

一个想法是将String答复和Int答复都编码为单个Receive行为。例如:

case object GetInteger
case object GetString

// ...

def handleMessages(intReplies: Set[Int] = Set(), strReplies: Set[String] = Set()): Receive = {
  case GetInteger =>
    router ! Broadcast(GetInteger)
  case GetString =>
    router ! Broadcast(GetString)
  case i: Int =>
    val updatedInts = intReplies + i
    if (updatedInts.size == numRoutees) {
      println("result = " + updatedInts.mkString("[", ",", "]"))
    }
    become(handleMessages(updatedInts, strReplies))
  case str: String =>
    val updatedStrings = strReplies + str
    if (updatedStrings.size == numRoutees) {
      println("result = " + updatedStrings.mkString("[", ",", "]"))
    }
    become(handleMessages(intReplies, updatedStrings))
  case x =>
    println("Not an Int or String: " + x)
}

def receive = handleMessages

请注意,我将GetIntegerGetString更改为case对象而不是case类,因为它们没有参数。这样做可以让您在最后加上括号(即,您可以使用GetInteger代替GetInteger())。

此外,如果您担心订单,请考虑使用有序集合,例如scala.collection.immutable.Seq而不是Set(无序)。