
客气地说,哈布雷的材料很少。 我决定翻译Manuel在他的博客中描述的一些反模式。 对于初次接触该框架的人来说,它们确实可能并不明显。
我想到我还没有写过这种非常频繁的反模式。 通常可以在刚开始使用actor模型的开发人员的代码中找到它。
有两种方法可以吸引过多的演员:
-开发了一个包含太多不同类型参与者的系统,其中许多不需要
-在不必要且效率低下的运行时中创建大量参与者
让我们详细看看这些选项。
演员类型太多
一般的想法是这样的:“我们有演员,所以一切都应该是演员。”
actor模型简化了编写异步应用程序的过程。 它通过在参与者内部提供同步代码执行的错觉来实现此目的-无需担心并发访问一个参与者的状态,因为只有一个参与者可以访问其状态,并且消息一次被处理。
但实际上,并非所有事情都需要异步完成。 专门与CPU相关联的方法调用(并且在没有完全阻塞CPU的意义上没有“阻塞”,例如,计算Pi值)不应异步执行。
我经常看到带有大量不同角色的代码相互交互,并且什么也不做,这在异步或同步执行中具有很大的优势。 在这些项目中,每个参与者必须存储相同的状态,或者在每个消息中将状态传递给他们。
这种方法有两个缺点:
“就性能而言,您一无所获。” 相反,与消息的创建及其传输相关的开销。
-对于每种类型的参与者和相关消息,系统变得更加难以理解和维护。
因此,在设计参与者系统时,您需要考虑什么实际上应该是异步的,基本上是这样的:
-调用外部系统(jvm外部)
-调用阻止操作(过时的API,大量计算等)
运行时演员过多
总的想法是这样的:“我们拥有的参与者越多,一切就会越快。”
实际上,参与者是轻量级的,
您可以在一台虚拟机中运行数百万个参与者。 是的,你可以。 但是有必要吗?
如果可以的话,并不代表您应该
简短的回答:并非总是如此-这取决于您与演员的关系。
如果您的系统有许多长期存在的参与者,每个参与者都包含一个很少的状态,并且不时相互交互,那么您很可能拥有一百万个参与者-这是一个合法的用例,在Akka的大力支持下。 例如,您可以创建一个具有大量用户的系统,其中每个用户都由一个actor代表。 一个纯净的Akka actor只占用300字节的内存,因此很可能在一台计算机上创建数百万个字节,并使它们工作而无需担心任何事情。 而且,如果最终您创建了很多演员或演员,它们的运气很大,它们将不再适合一台机器的内存,则群集分片将简化演员在多台机器上的分布。
但是,如果您有多种类型的参与者参与计算,例如解析XML文档,那么创建数百万个此类参与者就很可疑(直接或通过路由器都没有关系)。
处理器可使用固定数量的内核(硬件线程),Akka actor在基于线程池的ExecutionContext中处理消息。 默认情况下,这是基于Java 7中添加的ForkJoinPool的fork-join-executor。
但是,尽管具有技术优势,forkjoinpool并不是废除物理定律的魔术。 如果您有一百万个参与者,每个参与者都解析一个XML文档(已经加载到内存中)和4个硬件线程,那么系统的运行情况不会比只有四个参与者来解析这些XML文档(当均匀负载条件)。 实际上,您的系统将与4个参与者一起更好地工作,因为在计划和内存管理方面只有最小的开销。 顺便说一句,如果您的系统只有几个参与者,请检查您的线程池,这可能是试图将同一线程重用于同一参与者。
通常,如果创建很多角色,系统将无法更快地运行。
无状态演员
Actor是正确面向对象的(不同于Java中的对象):Actor的状态从外部是不可见的,并且它们通过消息进行通信。 打破封装是不可能的,因为您无法在演员工作期间调查演员的状态。 这就是演员的全部要点:他们给您一种安全的空间的错觉,在其中一个接一个地依次执行消息,使您可以在演员内部使用可变状态,而不必担心比赛条件(竞赛条件-大约每人)(嗯,几乎不用担心:最主要的是不要让状态泄漏)。
这就是为什么至少可以说使用没有状态的演员有些奇怪的原因。 除了那些控制观察者系统层次结构的大部分内容(例如,设置备份管理器)的执行者之外,执行者实际上是设计用于处理具有状态的长计算。 说长话,我的意思是演员将在一生中处理几条消息,根据他的情况产生不同的结果,而不是一次计算。 对他们来说,期货是一个很好的抽象:它们允许异步代码执行,可以组成并具有故障处理机制,非常适合用于与网络或磁盘相关的计算(或非常繁重的处理器任务)的情况。 它们还与Akka演员很好地集成在一起(使用管道模式)。
一般而言:如果您没有状态,请不要使用actor-它们不是为此目的而设计的。