我们发现一个问题,即共享BalancingDispatcher的一组Actors如果没有收到其他消息就会停止获取ReceiveTimeout消息。首先,组中的每个actor都按预期接收ReceiveTimeout,但很快就会得到它们的Actors数量下降,直到只有一个actor获得它为止。
如果有其他消息进入,似乎可以防止这种情况发生。只有当Actors循环一段时间后才会获得除ReceiveTimeout之外的任何内容。
以下是一个例子:
// Akka version 2.1.1, Scala version 2.10.0
import akka.actor._
import scala.concurrent.duration._
import scala.collection.mutable
case class TimeoutReceived(actor: String, timestamp: Long)
class TimeoutPool(system: ActorSystem) {
val timeouts = new mutable.MutableList[TimeoutReceived]
class TimeoutActor extends Actor {
override def preStart() = {
super.preStart()
context.setReceiveTimeout(100 milliseconds)
}
def receive = {
case ReceiveTimeout =>
println(System.currentTimeMillis() + " ReceiveTimeout " + self.path)
timeouts += TimeoutReceived(self.path.name, System.currentTimeMillis())
}
}
val actors: Iterable[ActorRef] =
for (x <- (0 to 9).toList) yield {
system.actorOf(Props(() => new TimeoutActor, "dispatcher"),
"example.actor" + x)
}
}
因此,您可以使用"dispatcher.type = BalancingDispatcher"
这样的配置启动一个并观察println输出。在不久之前,只有一个Actors会发出“ReceiveTimeout”输出。
这是一个演示该问题的测试类,并且还显示Actors尚未关闭:
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
import org.scalatest.FunSpec
import org.scalatest.matchers.ShouldMatchers
class ExampleSpec extends FunSpec with ShouldMatchers {
describe("Balancing dispatcher example") {
it("actors sharing dispatcher stop getting ReceiveTimeouts (except for one)") {
val system = ActorSystem("TimeoutExample", ConfigFactory.parseString("dispatcher.type = BalancingDispatcher"))
val pool = new TimeoutPool(system)
// spin until we've accumulated 50 timeouts
while(pool.timeouts.length < (50)) { Thread.sleep(500) }
// grab the last cycle of ten that we recorded
val lastTenTimeouts = pool.timeouts.toList.drop(40).take(10).map(_.actor.takeRight(1))
// have the actors shut down? No:
pool.actors.forall(_.isTerminated == false) should be (true) // will pass
// did each of the 10 actors get a timeout in the last cycle?
lastTenTimeouts.distinct should have size(10) // will fail with size 1 or 2.
system.shutdown()
}
}
}
将“BalancingDispatcher”更改为“Dispatcher”,测试将通过。
这是Akka中的错误,还是因为某种原因将ReceiveTimeouts与BalancingDispatcher一起使用无效?
答案 0 :(得分:2)
在Akka中,所有共享一个BalancingDispatcher的角色也会共享一个邮箱,这可能会导致您上面描述的情况。
如ScalaDoc for the BalancingDispatcher中所述:
基于执行程序的事件驱动调度程序,它将尝试将工作从繁忙的actor重新分配给空闲的actor。假设使用此调度程序的同一实例的所有actor都可以处理已发送给其中一个actor的所有消息。即actor属于一个actor池,对于客户端,无法保证哪个actor实例实际处理给定的消息。
虽然这种实现中使用的技术通常被称为“工作窃取”,但实际实施可能最好被描述为“工作捐赠”,因为工作被盗的行为者是主动的。