为啥在actor中创建actor是危险的
Posted
技术标签:
【中文标题】为啥在actor中创建actor是危险的【英文标题】:Why creating an actor within actor is dangerous为什么在actor中创建actor是危险的 【发布时间】:2017-11-16 03:07:19 【问题描述】:akka documentation 明确表示在这样的actor中创建actor是危险的:
class ActorA extends Actor
def receive = ???
final class ActorB extends Actor
def receive =
case _ =>
val act = context.actorOf(Props(new ActorA))
我了解 Actor 的 apply 方法正在接受创建 Actor 的 this
引用。但我无法理解(也找不到任何示例)为什么这是有害的以及它会导致什么问题?
【问题讨论】:
看来这篇好文章可以帮到你:cakesolutions.net/teamblogs/… @Leonard,谢谢我看到那篇很棒的文章,但是,IMO,它没有解释“为什么它有害”。请注意,其中一位读者“Landlocked”提出了同样的问题 我发现了这个:groups.google.com/forum/#!topic/akka-user/AnWHbESgCHQ 我认为 sender() 示例给出了很好的解释 @thwiegan 发件人示例是一个不好的解释,因为此代码就像您将发件人包装到Future
中一样。这个例子没有解释一个主要的问题,即在Props(new)
是break the actor encapsulation
的actor中创建一个actor。
@Leonard 我认为它在某种程度上与您发布的文章相结合。 sender()
示例显示,如果您将状态泄漏到参与者之外会发生什么。您发布的文章显示,使用 Props(new) 会泄漏状态(由于 scala 编译器行为,通过泄漏 this
引用)并因此破坏了参与者封装。
【参考方案1】:
让我们稍微调整一下您的示例
class ActorA(str:String) extends Actor
def receive = ???
final class ActorB extends Actor
def receive =
case _ =>
val act = context.actorOf(Props(new ActorA("hidden")))
使用 Actor 的大多数常见用例是处理故障转移和监督,当一个 Actor 失败并需要重新启动时,Actor 系统需要知道如何做到这一点。使用 Props(Props(new ActorA)) 时,你已经通过自己处理隐藏了“hidden”的参数值。
如果您声明如何创建演员的实例,而不是这样做,那么演员系统将确切地知道在重新创建演员时它需要做什么 - 即创建一个带有“隐藏”构造函数参数的 ActorA 实例。
即使是没有参数的 Actor 示例
context.actorOf(Props(new ActorA))
不推荐在另一个actor中实例化actor的这种方式,因为它鼓励关闭封闭范围,导致不可序列化的Props和可能的竞争条件(破坏actor封装)。
【讨论】:
我不确定我得到你了,你能举个例子说明它会有害吗?【参考方案2】:我相信我们混淆了创建和声明。医生说
Declaring one actor within another is very dangerous and breaks actor encapsulation. Never pass an actor’s this reference into Props!
所以问题是声明,而不是创建! 让我们看看Java的:
public class MyActor extends AbstractActor
@Override
public Receive createReceive()
return ReceiveBuilder.create()
.match(String.class, handleString())
.matchAny(x -> unhandled(x))
.build();
private FI.UnitApply<String> handleString()
return message -> sender().tell("OK", getSelf());
class MyOtherActor extends AbstractActor
@Override
public Receive createReceive()
return ReceiveBuilder.create()
.match(String.class, handleString())
.matchAny(x -> unhandled(x))
.build();
private FI.UnitApply<String> handleString()
return message -> sender().tell("OK-Inner", getSelf());
现在,如果 MyOtherActor 是一个普通类,我们只能从 MyActor 的实例中实例化它:
MyActor actor = new MyActor();
MyActor.MyOtherActor otherActor = actor.new MyOtherActor();
这意味着 MyOtherActor 的构造函数依赖于 MyActor 的实例!
现在,如果 Props 应该包含演员的“工厂”。他们需要一个工厂方法。如果我们的 MyOtherActor 像我们在这里所做的那样声明,那么我们的 props 将如下所示(ish):
MyActor actor = ??? // how did you even get a reference to the actor and not the actorRef in the first place!
Props otherActorProps = Props.create(MyActor.MyOtherActor.class, () -> actor.new MyOtherActor());
然后砰,踢球者来了!现在您的otherActorProps
包含对actor
的引用,即您已经关闭了可变状态!如果出于某种原因actor
“死亡”,您的道具仍将引用它,从而导致各种奇怪。
还有一个问题是如何首先获得对actor
的引用,而不是actorRef
恕我直言,这就是文档所指的,而不是在另一个演员中“创建”(即实例化、生成)演员的事实:这绝对是正常的,这是 akka 的例行操作(这就是为什么你可以这样做@ 987654330@和actorSystem.actorOf(...)
【讨论】:
【参考方案3】:警告在文档中,因为很容易意外关闭创建actor的状态,包括它的this
指针(你不应该在基于actor的代码中使用它)。
根据我的经验,我经常看到将props
方法放入演员的伴生对象中:
object ActorA
def props() = Props(new ActorA)
这样做可以确保返回的Props
不会关闭参与者的状态。
class ActorB extends Actor
def receive =
case _ =>
val actorB = context.actorOf(ActorA.props)
...
对于不接受构造函数参数的actor来说可能性不大,但是一旦参数发挥作用,你需要小心关闭内部状态。
【讨论】:
以上是关于为啥在actor中创建actor是危险的的主要内容,如果未能解决你的问题,请参考以下文章