该系列译自 Rerun.me, 并在原作者的基础上有所改动。本篇为该系列的开篇—— Actors 简介。
任何写过多线程程序的人都知道管理多线程应用程序是极其困难和痛苦的。这里『管理』是因为在刚开始时你感觉可能是简单的,并且在你开始看到性能提升后会觉得很有趣。然而,当你发现你并不能从子任务的错误中轻松恢复过来或者你发现那些僵尸 bug 很难重现,亦或是你的性能管理器告诉你你的线程在写共享状态前被阻塞了很久从而产生了资源浪费,以上这些都会让你头痛不已。
我在这里不会涉及 Java 的并发 API 及其应用集合如何使得并发变得更容易和更好,因为我相信如果你已经在读这篇文章了,你很可能需要对子任务更多的控制,亦或简单地因为你不喜欢使用锁和同步阻塞调用,并喜欢一种更高层次的抽象。
在这一系列的 Akka Notes 中,我们将会使用一些简单的 Akka 示例来探索 Akka 这个工具包中各种各样的特性。
何为 Actor
Akka Actor 遵循 Actor model (废话…)
举个例子,Actors 就像普天之下的芸芸众生,但在 Actors 的世界中,人与人之间并不能直接说话,他们通过邮件来通信。下面我们通过几个小节对其进行扩展。
1. Message - 消息
以两个人为例——一个睿智的老师和一个普通学生,学生每天早上给老师发送一封邮件,老师则给他回复一封富含哲理的引用。这里有几点需要注意:
- 学生发送邮件。一旦发送出去,邮件不能被再次更改编辑(消息不可变), 这是消息天然的不可变特性。
- 老师在他想检查邮箱的时候就可以检查邮箱。
- 老师会回复一封邮件(同样邮件内容发出去之后也是不可变的)。
- 学生可随时检查他自己的邮箱。
- 学生发送邮件出去后并不等待老师的回复(即不会阻塞)。
以上这些加起来就是 Actor 模型的基本组件了——消息传递,如下图所示。
2. Concurrency - 并发
现在让我们设想一下有3个睿智的老师和3个学生的情形——每个学生发一封邮件给所有老师。那么这种情况如何处理?其实这时候并没有实际的变化,每个人仍然有他自己的邮箱。
这里有一个微妙的地方需要指出来:默认情况下,在邮箱当中的邮件按照他们到达的时间顺序被依次读取/处理。
在内部实现中,Mails 默认是一个 ConcurrentLinkedQueue, 因为没人会去等待这封邮件,所以它是一个简单的非阻塞消息。实际上还有一些 bounded 和 priority based 的内建 Mailboxes, 另外我们其实还可以自己定义新的 mailbox.
3. Failover - 故障转移
设想有三个来自不同院系的老师——历史,地理和哲学。历史老师回复了一条 note 给过去一个事件,地理老师回复了一处有趣的地方,哲学老师则回复了一条 quote. 每个学生给每个老师发送消息并且得到相应的回复。学生并不关心是哪个院系的老师返回了消息。倘若某一天,有个老师生病倒下了怎么办?那么至少需要一名老师来 handle 发送给院系的邮件。在这种情况下,另外一名院系的老师勇敢地站了出来并接下了收邮件这个工作,如下图所示。
这里有几点需要指出:
- 有可能存在一个 Actors 池,每个 Actor 用来做不同的事情。
- 每个 Actor 执行某件任务后可产生 exception, 它自己并不能自行恢复。这种情况下 可以创建一个新的 Actor 来取代原来的 Actor. 当然你也可以选择忽略这个特定的消息并且处理剩下的消息。这称作 Directives, 我们后续会继续讨论这个。
4. Multitasking - 多任务
转折点来了,设想如果学生想要考试分数的话,每个老师也能通过 mail 发送。类似的,Actor 应该能 handle 不止一种消息类型。
5. Chaining - 链
如果学生并不想接收3封邮件,而是想接收一封紧凑的最终邮件呢?我们同样可以使用 Actors 达成这一目标,我们可以将三个老师链到一起形成一个层次结构。在我们后面讨论 Supervisors 和 Futures 时再回头看看这一点。
Actor Messaging
这一节对 Actor 的消息机制做一些简介,将以上学生和老师的比喻映射到 Actor Model 中,如下图所示:
学生和老师成为了我们的 Actors, Email Inbox 则充当了 MailBox 这个组件,请求和回应不可更改,它们都是不可变对象。最后,Actor 中的 MessageDispatcher 组件管理着 mailbox 并且将这些消息路由到各自的 Mailbox 中。
文字的介绍已经够多了,在 Akka Notes 2 - Actor Messaging 中我们将结合代码进行实战练习以便深入理解 Actor 的消息机制。
Reference
- ConcurrentLinkedQueue - 非阻塞算法在并发容器中的实现
- ConcurrentLinkedQueue - 聊聊并发(六)——ConcurrentLinkedQueue的实现原理分析