"Actor"是一个封装了状态和行为的对象。它们之间的通信是通过各自专属的“邮箱”实现信息的交换。可以说“Actor”这种机理是最严格的面向对象编程。然而它的形式可以类似于人类,我们以“Actor”方式建模解决问题时,可以想象为有一群人并将各种任务分配给他们,梳理他们在组织结构中的作用并考虑如何升降他们(庆幸的是这样做不需要像面对人那样存在心态问题和道德问题)。这样就能把它做为软件实现的抽象架构。
注意
Actor是一个重量级的系统,它会分配1~N个线程用以建立每一个逻辑应用。
层次结构
就象古代的家政结构,Actor自然形成了层次结构。在程序中管理特定的功能Actor期望把任务拆分的更小更易管理。以此为目标启动子Actor实施管理。在这一节我们将专注于基础概念,而监管功能将在后继解释。重点是理解每个Actor都由一个监管它的Actor创建。
Actor的典型特征是把任务拆分,使它小到足以委托完成单一的工作。这样做不仅使任务本身的结构清晰,而且使团队中的Actor明确各自该处理的消息,该如何反应,以及该如何应对故障。如果Actor没有措施处理特定情况,它会发送相应的错误消息给主管以帮助解决。而递归的结构使错误返馈到正确的等级上。
相对于分层设计的软件设计不泄漏任何失败的防错式编程,它把问题传递给正确的人,用一个更好的解决方案,总比把一切隐藏起来强。
而这样设计也有一个难点即如何决定由谁担当主管。目前没有一个最好的解决方案,但有一些指导方针可以参考:
- 一个Actor管理着任务,另一个在执行。例如处理子任务。那么管理者是子任务的主管。因为管理者可以知道哪些错误类别并能捕捉到它们。
- 一个Actor有很重要的数据(尽可能避免它的数据丢失),它适合去主管源自于它的任何可能危险的子任务。根据请求的性质去创建新的子任务,这样简化了状态管理收集的响应。这是已知Elang的“错误内核模式”。
- 一个Actor依赖于其它Actor运行的职责,在收到终端消息后需要观察其它的Actor的存在及行动。这并不同于主管,观察者并不影响主管策略,同时注意并不能仅从功能依赖来决定Actor在层次中的位置。
然而这些规则总是有例外,但无论遵守或破坏总要给自已一个理由。
配置集成
Actor系统作为一个多个成员统一合作系统自然把共享管理的工具作为一个单元象调度服务,配置 ,日志等。多个配置不同的Actor系统可以共存在同一个JVM中,因为在Akka中没有全局共享的状态。伴随着Actor系统间的从一个点或网络连接的透明传输,可以发现Actor系统的构成在一个功能层次结构中。
Actor最佳范例
- Actor就象出色的同事,高效的完成工作不会毫无必要的打扰它们或占用资源。这意味着采用事件驱动模式,被动式编程来处理事件以及生成(多个)响应。Actor不会阻塞(如被动等待被占用的线程)外部实体,它可能是一个锁,或一个网络socket等,除非不法避免。这种情况见下文。
- Actor之间不传递可变化对象,因此采用不可变化消息。如果打破Actor的封装,对外暴露可变状态,那就回到了普通JAVA并发领域所有的缺点。
- Actor被做成封装状态和行为的容器,这就要不定期的用消息发送状态(这可能是用Scala吸引人的地方)。风险之一是偶尔在Actor之间共享了可变状态,这完全违反了actor模型,对程序员来说是很好的经验。
- 高级的Actor是容错核心内容,所以谨慎的创建它们并真正实现层次体系。这有利于关注到故障处理(兼顾配置与性能的粒度)也利于减少对监测Actor的负担,如果过度使用它会成为一个冲突点。
精心管理阻塞
在某些情况下阻塞不可避免,比如把一个线程休眠不确定的时间,等待外部事件发生。比如传统的RDBMS驱动和消息API以及网络IO后的潜在原因。面对这个问题,你可能会将阻塞在未来调用代替现在情况,这种策略很简单:你很可能会发现在应用程序运行时增加负荷时产生瓶颈或耗尽内存线程。
下面的列举了“阻塞问题”的解决方案建议:
- 当一个Actor执行阻塞调用时(或设置一个Actor管理路由时),确保一个线程池专门用于它且足够大。
- 当将来调用阻塞时,在任何时候需要确保一个调用上限数量(提交无数量限制的任务会耗尽内存或线程限制)
- 当将来调用阻塞时,提供一个硬件上运行程序有一个数量有限的线程池。
- 用单个线程管理一组阻塞资源(如NIO选择器驱动多个通道)和把分派事件发生时作为Actor的消息。
第一种可能是特别适合资源是单线程的,像传统数据库处理一次只能执行一个待处理的查询并使用内部同步来确保这一点。常见的模式是创建N个路由Actor,每个包含一个DB连接和处理查询发送到路由。N必须针对最大吞吐量,这将取决于什么硬件上部署DBMS。
注意
配置线程池最好委托给Akka,简单的配置application.conf 并实例化Actor系统
你不应该关心什么
一个Actor系统管理着配置支持运行需要的内容。在一个系统中可能有百万级Actor运行,看起来非常丰富,它们运行时每个实例约占300字节的空间。实际上在大型系统中消息的精确处理顺序并不是程序作者所知的,这并不是目的。退一步讲释放也是Akka一个繁重的任务。