1.5 关于 Akka Actors 和 ActorSystems
对于讨论过的每个概念,Akka 都有一组组件:提供了一定程度的重定向的 actor 地址, 用来临时保存消息,当然还有 actor 本身的邮箱。 我们来简单看看需要的支撑组件以及这些组件如何相互联系,讨论 Akka 如何抽象了底层的线程机制,以及 Actors 如何运行在这种抽象上。
对于 actor 是什么,我们已经在之前的章节中在概念层次上谈论了很多。 我们确认了为了得到消息传递风格,我们需要如下做:
- 没有可变共享数据结构,
- 传递的是不变的消息,
- 异步消息发送。
重要的是要记住,当你使用工具箱或框架是,你需要知道的是能清楚地勾画出工具箱为你做的事。 如在图1.10中看到的,actor 是你可以发送消息给它的对象,关心消息的投送和接收不是你的责任。 你的代码可以只关心:actor 有行为,当收到特定消息时,做特定的事。 且通过参与代表消息语言的协议(类似 DSL 而不是方法签名)来与其他 actors 合作。
我们来看看 Akka 如何实现 actors 的,以及与我们讨论过的概念相对的组件: actors, 地址,和邮箱。 Akka 使用 Scala 语言实现的,提供了构建 actor 的 Java 和 Scala 的API,本书中的例子使用 Scala 语言和 Scala API。 在 Scala API 中,Akka 提供了 Actor trait,你可以扩展它以构建自己的 actor。 TicketingAgent 可以实现为 Akka Actor,这是从 Actor trait 扩展的,将游戏(TODO?)状态和要售的票保存在内部队列中。 我们之前所谓的地址, 在 Akka 中称为 ActorRef,即 actor reference 的简称。 对 TicketingAgent 的引用 ActorRef 被打印室用来作为发送消息给 TicketingAgent 的地址。 在 Akka 中 ActorRef 有各种形式,大部分都隐藏看不到。 使用API,你只接触到 ActorRef 类。
我们来看看图 1.10 中的简单例子,描绘了我们的代码要做什么,以及 ActorSystem 会帮我们做什么。 如你看到的,我们只需要要一个 TicketingAgent actor 的引用,一旦有了,简单地向它发送购票请求就可以了(步骤 1 和 5) ActorSystem 为我们屏蔽了如下的细节:actor 如何处理我们的请求,信箱在哪,是否送达,等。
图 1.10 活动中的 Actor 请求周期
当然,我们之前讨论的规则都适用:我们不会阻塞,等待请求被处理,我们把共享状态嵌入在请求中, 对买票请求的实际处理不会锁住任何东西,也不会访问任何会阻止并行处理请求的共享状态。
那你如何获取层次中一个 actor 的引用?这就引出了 ActorPaths。 你可以把 actor 的层次与 URL 中的路径结构进行对比。 每个 actor 都用名字。 层级中每一级的名字必须是唯一的,两个同级 actors 名字不能相同(如果没给名字,Akka 会为你生成一个, 但是最好还是给每个 actor 一个名字)。 所有 actor 引用可以直接由 actor 路径定位,无论是绝对路径还是相对路径,而且要遵照 URI 的语法。 actor 路径类似 URI 路径,从 scheme 开始,然后是 scheme 相关的部分,如图 1.11 所示:
Figure 1.11 ActorPath URI 语法
在上边的例子中,打印室 actor 'poffice1' 可以通过使用路径 ‘TicketingAgent2’ 来引用 ‘TicketingAgent2’ TicketingAgent2 可以用 ‘../TicketingAgent3’ 引用它的同级节点。 守护 actor 总是叫做 ‘user’.
出现父节点的概念不只是为了整齐:Akka 中最重要的概念之一,监控,就是层级的功能:每个 actor 都自动成为子节点的监控者。 这意味着,当子 actor 崩溃,父节点来决定采取何种策略来纠正问题。 这使得问题可以顺着层级上升,上层的监控者可以处理这种更为全局的问题,这这是它关注的。
总之,Akka 阻止通过构造函数直接创建 actor,因为那会绕过 actor 层次;通过构造函数直接创建 actor 使得可以直接访问 actor 的方法, 这破坏了并发消息传递风格的规矩。 actor 系统创建 actor 的引用,提供定位 actor 引用的通用方法,还提供了根 actor 来创建 actors 层级,并连接运行时组件到 actors。
核心的 Actor 操作: 看待 actor 的另一种方式是描述其支持的操作: Akka actor 有四种核心操作:
- 创建:一个 actor 可以创建另一个 actor。 在 Akka 中,actors 是 actor 系统的一部分,actor 系统定义 actor 层级的根,创建顶级的 actors。 每个 actor 都可以创建子 actors。 Actors 的拓扑是动态的,依赖于哪个 actors 创建其他 actors,哪个地址用来与它们通信。 
- 发送:一个 actor 可以给另一个 actor 发送消息。 消息是异步发送的,使用地址来发送给与特定信箱相连的 actor。 
- 变化:actor 的行为可以动态改变。 actor 一次接收一个消息,可以指定以不同方式处理下一个消息,即改变行为,这在后边的章节会看到。 
- 监控: actor 监控其 actor 层级中的子节点,管理可能发生的失败。 如我们在第三章中会看到的,这里将消息传递和错误处理明确区分。