在我们开始之前:这个问题涉及已废弃的“类型化Actor”模块。该模块很快将被akka-typed所取代,它是对该问题的更好解决方案,避免了下面解释的缺点 - 如果你对类型化Actor感兴趣,请确保查看akka-typed!
我将列举使用您所提到的类型化Actor实现的一些缺点。请注意,我们刚刚合并了一个新的
akka-typed模块,它为Akka Actors带来了类型安全。就本文而言,我不会深入探讨开发类型化版本是如此艰巨的原因,现在让我们回答“为什么不使用(旧的)类型化Actor”的问题。
首先,它们从未被设计为工具包的核心。它们建立在Akka提供的消息基础设施之上。请注意,由于这种消息基础设施,我们能够实现位置透明度和Akka的众所周知的性能。它们大量使用反射和JDK代理将方法转换为消息发送,并从消息接收者中翻译回来。这非常昂贵(时间上),与普通的Akka Actors相比,性能下降了约10倍。请参见下面的“乒乓”基准测试(使用两种风格实现,发送方告诉Actor,Actor回复- 100,000次):
Unit = ops/ms
Benchmark Mode Samples Mean Mean error Units
TellPingPongBenchmark.tell_100000_msgs thrpt 20 119973619.810 79577253.299 ops/ms
JdkProxyTypedActorTellPingPongBenchmark.tell_100000_msgs thrpt 20 16697718.988 406179.847 ops/ms
Unit = us/op
Benchmark Mode Samples Mean Mean error Units
TellPingPongBenchmark.tell_100000_msgs sample 133647 1.223 0.916 us/op
JdkProxyTypedActorTellPingPongBenchmark.tell_100000_msgs sample 222869 12.416 0.045 us/op
基准测试保留在akka/akka-bench-jmh中,并使用OpenJDK JMH工具通过sbt-jmh插件运行。
其次,使用抽象方法来处理分布式系统并不是一个好的方式(哦,我还记得RMI……让我们不要再去那里了)。使用这种“看起来像方法”的方法会使您停止思考消息丢失、重新排序以及在分布式系统中可能发生的所有事情。它还鼓励(使“做错事情变得太容易”)使用诸如
def getThing(id: Int): Thing
这样的签名——这将生成阻塞代码——这对性能来说是可怕的!您真的希望保持异步和响应,这就是为什么在尝试与这些(基于代理的)类型化actor正确地工作时,您最终会得到大量的future。
最后,你基本上会
失去一个主要的Actor能力。Actor可以执行的三个经典操作是1)发送消息2)启动子Actor3)
根据接收到的消息更改自己的行为(请参见Carl Hewitt关于
Actor Model的原始论文)。第三种能力用于优美地建模状态机。例如,您可以使用普通的akka actors说
become(active)
然后
become(allowOnlyPrivileged)
,以在
receive
实现之间切换-使有限状态机实现(我们也有一个
FSMs DSL)非常愉快。您无法在JDK代理的类型化Actor中很好地表达这一点,因为您无法更改公开方法的集合。一旦您开始思考和建模使用状态机,这是一个重大的缺点。
新的希望(第1集):请查看由Roland Kuhn编写的
即将发布的akka-typed模块(预览版很快会包含在2.4版本中),我相信您会喜欢那里的类型安全性。而且,该实现最终将比当前的无类型actors更快(由于答案已经很长,在这里省略了实现细节 - 简短的版本:基本上我们将通过新的实现去除大量分配)。
我希望您会喜欢这个详尽的答案。请随时在此处或akka-user - 我们的官方邮件列表中提出后续问题。祝您编程愉快!