写过多线程的人都不会否认,多线程应用的维护是件多么困难和痛苦的事。我说的是维护,这是因为开始的时候还很简单,一旦你看到性能得到提升就会欢呼雀跃。然而,当你发现很难从子任务的错误中恢复或者有些僵尸BUG很难复现再或者你的分析器显示你的线程在写入一个共享状态前大部分时间都浪费在阻塞上面的时候,痛苦降临了。
我刻意没提Java的并发API,以及它里面的集合类使得多线程编程变得多么轻松简单,因为我相信既然你们点进了这篇文章,那就说明你希望能更好地控制你的子任务,或者你就是不喜欢使用锁以及同步块,希望能有一种更高层次的抽象。
在本系列的Akka笔记中,我们将通过一些简单的Akka例子来体验下它的诸多特性。
什么是Actor?
Akka中的Actor遵循Actor模型。
你可以把Actor当作是人。这些人不会亲自去和别人交谈。他们只通过邮件来交流。
我们来稍微解释下这点。
1. 消息传递
假设有两个人——一个聪明的老师和一个学生。学生每天早上都会发一封邮件给老师,而老师则会回复一句名言。
需要注意的地方:
1. 学生发送邮件。一旦发送成功,邮件不能再修改。这天然就具备了不可变性。
2. 老师会自己决定何时检查邮箱。
3. 老师还会回复一封邮件。
4. 学生会自己决定何时检查邮箱。
5. 学生不会一直等待回信(非阻塞的)。
这就可以总结出Actor模型的一个基本特征——消息传递。
2. 并发
现在,想像一下3个聪明的老师和3个学生——每个学生都会向所有老师分别发送笔记。这会发生什么?事实上没有任何改变。每个人都有自己的邮箱。这里有一点值得强调一下:
默认情况下,邮箱中的邮件是按它们到达的顺序进行处理的。
本质上这就是一个ConcurrentLinkedQueue。由于没有人在等待邮件,这就是一个非阻塞的消息。(Akka内置包含许多种邮箱实现,包括有界的以及基于优先级的)。事实上,我们自己也能开发一个。
3. 失败处理
想像下这三个老师是不同系的——历史,地理以及哲学。
历史老师对过去的某个事件进行了评论,地理老师则发送了一个很有意思的地方,而哲学老师回复了一句名言。每个学生都分别给各个老师发送邮件并收到回信。学生并不关心邮件到底是系里的哪个老师回复的。如果有一天有个老师生病了呢?系里至少得有一个老师在处理邮件才行。这样的话,系里的另一位老师就会顶上这项工作。
需要注意的地方:
1. 会有一个Actor的池,每个Actor会处理不同的事件。
2. Actor做的事情可能会抛出异常。它自己无法从中恢复。这种情况下,需要再生成一个新的Actor来顶替它。换句话说,这个新的Actor会忽略刚才那条消息,继续处理剩余的消息。这些也被称为指令(Directive),后面我们会再讲到它们。
4. 多任务
我们假设下每个老师都会通过邮件来发送考试成绩,如果学生这么要求的话。类似的,Actor也可能会处理多种类型的消息。
5. 消息链
那如果学生不想收到多封邮件,而是一封该怎么办呢?
Actor同样可以完成这个。我们可以将老师进行分层。后面我们讲到Supervisor和Future的时候会再回来讲下这点。
应Mohan的要求,我们把类比的实体和Actor模型中的组件做一下映射。
学生和老师都是我们的Actor。收件箱就是Mailbox组件。请求和响应是不可修改的。它们都是不可变对象。最后,Actor中的MessageDispatcher组件会管理邮箱并将消息路由到对应的Mailbox中。
说得够多的了,我们来写下代码吧…
未完待续…