Java 中的 Akka 类型演员

Akka typed actors in Java

我不明白为什么不在 Akka 中使用 TypedActors。使用反射(嗯.. instanceof)来弥补 Java 中模式匹配的不足是相当丑陋的。
据我了解,TypedActors 应该就像您软件的 "Akka world" 和 "Non Akka world" 之间的门。但为什么我们不放弃所有 OO 原则而只使用反射!
你为什么不想使用演员并确切地知道它应该回应什么?或者,为了 Akka 保留 actor 模型,为什么不创建一个使用双分派的消息层次结构,以便在 actor 中激活正确的方法(我知道你不应该将 Actors 作为参数传递,而是使用 ActorRef)。
免责声明:我是 Akka 和这个模型的新手,我还没有使用 Akka 编写过一行代码,但光是阅读文档就让我头疼。

Typed Actors 为您提供了在您的域中定义的静态契约——您可以命名它们的消息(将被委托给底层实现并异步执行)在您的域中有意义的操作,避免在你这边使用反射(TypedActors 在幕后使用 JDK 代理,所以反射仍然在进行,你只是不必担心它,并且你可以根据传递的参数进行类型检查到活跃的 object/typed actor 及其 return 类型。documention 对此非常清楚,但我知道对于那些刚接触基于 actor 的并发的人来说,额外的例子总是有帮助的,所以请随意如果您仍然难以摸索差异,请多问 questions/comments。

在我们开始之前:问题是关于 deprecated "typed actors" module. Which will soon be replaced with akka-typed 的,这是对问题的一种更高级的理解,它避免了下面解释的缺点 - 请做一个如果您对类型演员感兴趣,请查看 akka-typed!


我将列举使用您提到的类型化 actor 实现的一些缺点。但是请注意,我们刚刚合并了一个新的 akka-typed 模块,它将类型安全带回了 akka actor 的世界。为了这个post,我不会深入探讨开发打字版的挑战如此艰巨的原因,让我们现在回答"why not use the (old) typed actors"的问题。

首先,它们从未被设计为工具包的核心。它们建立在 Akka 提供的消息传递基础设施之上。请注意,由于消息传递基础设施,我们能够实现位置透明和 Akka 众所周知的性能。他们大量使用反射和 JDK 代理来转换方法和消息发送。这是非常昂贵的(时间方面),并且与普通 Akka Actor 相比,性能降低了大约 10 倍,参见下面的 "ping pong" 基准(使用两种样式实现,发件人告诉演员,演员回复 - 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 and run using the OpenJDK JMH tool, via the sbt-jmh 插件中。)

其次,使用方法对分布式系统进行抽象并不是一个好的方法(哦,我怎么记得RMI...让我们不要再去那里)。使用这样的 "looks like a method" 可以让你不再考虑消息丢失、重新排序以及分布式系统中可以 发生的所有事情。它还鼓励(使其“太容易做错事”)使用像def getThing(id: Int): Thing这样的签名——这会生成阻塞代码- 这对性能来说太可怕了!您确实希望保持异步和响应,这就是为什么您在尝试与这些(基于代理的)类型化 actor 正常工作时最终会遇到大量的 futures 的原因。

最后,你基本上失去了一个主要的 Actor 能力。 Actor 可以执行的 3 个规范操作是 1) 发送消息 2) 启动子 actor 3) 根据收到的消息改变它自己的行为(参见 Carl Hewitt 的原始论文 Actor Model). The 3rd capability is used to beautifully model state machines. For example you can say (in plain akka actors) become(active) and then become(allowOnlyPrivileged), to switch between receive implementations - making finite state machine implementations (we also have a DSL for FSMs) 合作愉快。你不能在 JDK 代理类型的 actors 中很好地表达这一点,因为你不能改变公开的方法集。一旦您开始使用状态机进行思考和建模,这是一个主要的缺点。

新希望(第 1 集):请务必查看由 Roland Kuhn 撰写的 the upcoming akka-typed module(即将包含在 2.4 版本中的预览),我很确定你会喜欢你在那里找到的类型安全明智的东西。而且,该实现最终将比当前的无类型 actor 更快(这里省略了 impl 细节,因为答案已经很长了——简短版本:基本上,由于新的实现,我们将删除大量分配)。

希望您会喜欢这个详尽的回答。请随时在此处或 akka-user - 我们的官方邮件列表的评论中提出后续问题。哈金快乐!

但是你们有没有意识到你们有很多公司,他们没有专业的开发人员,但是有一个大型基础设施可以根据我们的需要进行水平扩展,所以性能并不总是最好的“去为它”,而是响应式的、消息驱动的、弹性的和有弹性的,这要归功于我们拥有的类型演员,被对 Akka 或 Reactive 一无所知的开发人员使用 编程。

不要误会我的意思,我每天都在使用纯 Akka 类型,但对于交付团队,我们有这个使用类型化 actor 的框架,我们的消费者使用 POJO,而不知道他们正在编码一个反应系统。这是很棒的功能。