Akka:演员共享平衡调度停止接收超时
我们发现了一个问题,即共享一个BalancedDispatcher的一组参与者如果没有收到其他消息,就会停止收到ReceiveTimeout消息。起初,组中的每个参与者都会收到预期的ReceiveTimeout,但很快,获得它们的参与者数量就会下降,直到只有一个参与者获得它 如果有其他信息进来,似乎可以阻止这种情况发生。只有当演员循环一段时间,除了接收超时外,什么也得不到 下面是一个例子:Akka:演员共享平衡调度停止接收超时,akka,Akka,我们发现了一个问题,即共享一个BalancedDispatcher的一组参与者如果没有收到其他消息,就会停止收到ReceiveTimeout消息。起初,组中的每个参与者都会收到预期的ReceiveTimeout,但很快,获得它们的参与者数量就会下降,直到只有一个参与者获得它 如果有其他信息进来,似乎可以阻止这种情况发生。只有当演员循环一段时间,除了接收超时外,什么也得不到 下面是一个例子: // Akka version 2.1.1, Scala version 2.10.0 import a
// 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)
}
}
将“BalancingDispatcher”更改为“Dispatcher”,测试将通过
这是Akka中的一个错误,还是因为某种原因,将ReceiveTimeouts与BalancingDispatcher一起使用是无效的?在Akka中,共享一个BalancingDispatcher的所有参与者也将共享一个邮箱,这可能会导致出现上述情况 如下列文件所述: 一个基于执行器的事件驱动调度器,它将尝试将工作从繁忙的参与者重新分配到空闲的参与者。假设所有使用此调度器的同一实例的参与者都可以处理发送给其中一个参与者的所有消息。也就是说,参与者属于一个参与者池,对于客户端来说,无法保证哪个参与者实例实际处理给定的消息 尽管这种实现中使用的技术通常被称为“工作窃取”,但实际的实现可能最好被描述为“工作捐赠”,因为工作被窃取的参与者是主动的
此外,不鼓励使用BalancingDispatcher(除了真正的特殊情况)来支持路由器,甚至更好,使用普通参与者的定制工作分发。Sergiy,感谢您的回复。当然,我们已经读过了,在我们的用例中,“使用该调度器的同一实例的所有参与者都可以处理发送给其中一个参与者的所有消息”。。。但是关于“无法保证哪一个actor实例实际处理给定消息”的部分让我们大吃一惊。这种情况令人恼火的是,它确实会在最初的几个周期内分发消息,欺骗我们认为它是有效的,但正如您所指出的,这并不能保证。Endre-感谢您的提醒,事实上,我们对代码的修复是在一个最小的MailboxRouter中交换BalancingDispatcher。
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()
}
}
}