翻译自:http://doc.akka.io/docs/akka/2.3.8/general/actors.html
Actor 是什么?
前一章节关于 Actor 系统 解释了 actors 如何组成层级,且是构建应用的最小单元。这章只关注这样的 actor,解释你在实现它时会遇到的概念。更多深入细节的参考见 Actors(Scala) 和 Untyped Actors(Java) 。
一个Actor是一个容器,它包含了 状态,行为,一个 邮箱,子Actors 和一个 督导策略。所有这些封装在一个 Actor 引用 后面。当 Actor 终止时,会发生 这个。
Actor 引用
一个 actor 对象需要与外界隔离开才能从 actor 模型中获益。所以 actor 是以 actor 引用的形式展现给外界的,actor 引用可以被自由的无限制地传来传去。内部对象和外部对象的这种划分使得所有想要的操作能够透明:重启 actor 而不需要更新别处的引用,将实际 actor 对象放置到远程主机上,向另外一个完全不同的应用程序的 actors 发送消息。但最重要的方面是从外界不可能访问到 actor 对象的内部状态,除非这个 actor 非常不明智地将信息(内部状态)公布出去。
状态
Actor 对象通常包含一些变量来反映 actor 所处的可能状态。这可能是一个明确的状态机(e.g. 使用 FSM 模块),或是一个计数器,一组监听器,待处理的请求,等等。这些数据使得 actor 有价值,并且必须将这些数据保护起来不被其它的 actor 所破坏。好消息是在概念上每个 Akka actor 都有它自己的轻量线程,这个线程是完全与系统其它部分隔离的。这意味着你不需要使用锁来进同步访问,你只需要写你的 actor 代码,完全不需要关心并发问题。
在幕后,Akka 会在一组实际线程上运行一组 Actor,通常是很多 actor 共享一个线程,对某一个 actor 的调用可能会在不同的线程上进行处理。Akka 保证这个实现细节不影响处理 actor 状态的单线程性。
由于内部状态对于 actor 的操作是至关重要的,所以状态不一致是致命的。当 actor 失败并由其督导者重新启动,状态会进行重新创建,就象第一次创建这个 actor 一样。这是为了实现系统的“自愈合”。
视情况,一个 actor 的状态可以自动恢复到重启前,通过持久化接收到的消息并在重启后重演这些消息(见 持久化)。
行为
每次当一个消息被处理时,消息会与 actor 的当前的行为进行匹配。行为是一个函数,它定义了处理当前消息所要采取的动作,如果客户端被授权了则可以转发请求,否则拒绝。行为可能随时间而改变。因为不同的客户端随时间获得不同的授权,或因为 actor 可能进入“非服务状态”模式,稍后再回来。这些变化要么通过将它们放进从行为逻辑中读取的状态变量中实现,要么函数本身在运行时被替换出来,见 become 和 unbecome 操作。但是 actor 对象在创建时所定义的初始行为是特殊的,因为当 actor 重启时会恢复为这个初始行为。
邮箱
Actor 的用途是处理消息,这些消息是从其它的 actors(或者从 actor 系统外部)发送过来的。连接发送者与接收者的纽带是 actor 的邮箱:每个 actor 有且仅有一个邮箱,所有的发来的消息都在邮箱里排队。排队按照发送操作的时间顺序来进行,这意味着从不同的 actor 发来的消息在运行时没有一个固定的顺序,这是由于 actor 分布在不同的线程中。从另一个角度讲,从同一个 actor 发送多个消息到相同的 actor,则消息会按发送的顺序排队。
可以有不同的邮箱实现供选择,缺省的是 FIFO:actor 处理消息的顺序与消息入队列的顺序一致。这通常是一个好的选择,但是应用可能需要对某些消息进行优先处理。在这种情况下,可以使用有优先次序的邮箱来根据消息优先级将消息放在某个指定的位置,甚至可能是队列头,而不是队列末尾。如果使用这样的队列,消息的处理顺序是由队列的算法决定的,通常不是 FIFO。
Akka 与其它 actor 模型实现的一个重要差别在于当前的行为必须处理下一个从队列中取出的消息,Akka 不会去扫描邮箱来找到下一个匹配的消息。无法处理某个消息通常是作为失败情况进行处理,除非覆盖了这个行为。
子 Actor
每个 actor 都是一个潜在的督导者:如果它创建了子 actor 来委托处理子任务,它会自动地督导它们。子 actor 列表维护在 actor 的上下文中,actor 可以访问它。对列表的更改是通过创建 context.actorOf(...)
或者停止 context.stop(child)
子 actor 来实现,并且这些更改会立刻生效。实际的创建和停止操作在幕后以异步的方式完成,这样它们就不会“阻塞”其督导者。
督导策略
Actor的最后一部分是它用来处理其子 actor 错误状况的机制。错误处理是由 Akka 透明地完成,将 “督导与监控” 中所描述的策略中的一个应用于每个出现的失败。由于这个策略是 actor 系统组织结构的基础,所以一旦 actor 被创建了它就不能被修改。
考虑对每个 actor 只有唯一的策略,这意味着如果一个 actor 的子 actor 们应用了不同的策略,这些子 actor 应该按照相同的策略来进行分组,生成中间的督导者,又一次倾向于根据任务到子任务的划分来组织 actor 系统的结构。
当 Actor 终止时
一旦一个 actor 终止了,例如失败了并且不能用重启来解决,停止它自己或者被它的督导者停止,它会释放它的资源,将它邮箱中所有未处理的消息放进系统的“死信邮箱”,它将把它们作为 DeadLetter
转发到 EventStream
。而 actor 引用中的邮箱将会被一个系统邮箱所替代,系统邮箱会将所有新的消息作为 DeadLetter
重定向到 EventStream
。 但是这些操作只是尽力而为,所以不能依赖它来实现“保证投递”。
不选择简单地抛弃消息的理由是被我们的测试激发的:我们在事件总线(死信在这里传递)上注册了 TestEventListener
,将对接收到的每个死信记录日志警告,这对更快地发现测试失败很有帮助。很可能这个特性对其他用途也有帮助。
欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。