原文: The actor model in 10 minutes

CPU不能变得更快了。现在我们有了多核CPU,如果我要榨干现有硬件的每一分性能,我们就需要让代码并行运行。数十年的难以捉摸的bug让无数开发者头疼不已,thread并不是前进的方向。但是不用怕,时至今日我们有了优秀的替代者,而我今天就要向你展示其中之一:Actor模型。

模型

Actor模型是处理并行计算的概念性模型。它定义了系统组件行为和交互的通用规则。Erlang大概是使用此模型的最为有名的语言。我会试着专注模型本身而不是它在不同语言或lib中的具体实现。

Actor

Actor是计算的原始单位。它可以接受消息,并在此基础上做出某种计算。
这个思想与我们在面向对象语言里使用的东西极为相似:对象可以接受消息(方法调用)并且基于其接受的消息做出某些事情(调用的是哪个方法)。
但最大的差别在于,Actor之间是相互隔离的,它们不共享内存。Actor还可以轻松维持一个私有其他Actor无法直接改变的私有状态。

独木非林

单个Actor不算是Actor。它们必须成系统地出现。在Actor模型中,任何事物都是一个Actor,而且它们需要有地址,这样Actor之间就可以相互发送消息。

Actors的信箱

理解这一点很重要,尽管同一时间可以运行多个Actor,但是一个Actor只会顺序地处理消息。这意味着如果你要给同一个Actor发送三条消息,这些消息的处理只会一个接一个进行。要想让三条消息并行处理,你需要创建三个Actor,并且给每一个Actor都发送一条消息。

消息送达给Actor的形式是异步的,这就需要在处理消息时存储其他消息。信箱就是这么个地方,存储着这些消息。
image.png
Actor之间通过发送异步消息相互交流。这些消息直到被处理前会存储在其他Actor的信箱当中。

Actor的能力

当一个Actor接受到消息时,它可以做出如下3件事情之一:

  • 创建更多的Actor
  • 向其他Actor发送消息
  • 指定要对下一条消息做什么事

前两件事很直接,但是最后一件事就有趣了。
我之前说过Actor可以持有私有状态,“指定要对下一条消息做什么事”基本上就是在接受到下一条消息时定义出这个状态。或者说得更明白点,就是Actor会如何改变此状态。

假设我们有一个Actor,它的行为就像是计算器一样,初始状态就是数字0。当这个Actor接收到add(1)的消息时,它不会改变原有的状态0,而是指定在下一条消息接收时,这个状态会变成1

译者 有点像“Copy-On-Write”这种形式,不就地改变原有状态。

容错性

Erlang引入了“他错由他错”的哲学。这一思想就是说,你不应该防御性地写代码,不应该去预测未来可能发生的错误然后进行处理,原因很简单,因为你不可能面面俱到。
Erlang所做的只是任由程序崩溃,但是需要有人来监管这部分关键代码,这个人的唯一责任就是知道崩溃发生时应该做什么(比如把这块代码重设到一个稳定的状态),而正是Actor模型使这一切有可能成为现实。

每一行代码都是在某个processErlang中的Actor)的内部运行。这个process是完全孤立的,它的状态不会被其他process所影响。同时还存在一个监管者,它也是一个process(还记得我说过任何事物都是一个Actor吗?),可以在被监管的process崩溃时做出行动。

这样就可以构建出一个可以“自愈”的系统,也就是说如果Actor因为种种原因,走向了不可预知的状态,并且崩溃了,监管者就会介入,试着把它重新放入一致的状态中(有很多策略可以使用,最常用的一个就是把Actor重置为初始状态)。

分布式

另一个有趣的话题就是Actor模型不管我发送信息到何方,可以是本地也可以是远程的节点。

想想看,如果Actor仅仅是一块代码加一个信箱和一个内部状态,它能做的就是响应消息,谁会管它到底运行在什么机器上呢?只要我们想办法送达消息就行了。

这可以允许我们创建多机器的系统,而且在单点故障时可以帮我们进行恢复。

进一步学习以及更多资源

以上就是对这一概念模型的一个速览,很多优秀的语言都使用到了它,比如ErlangElixir,还有像AKKA(适用JVM)和Celluloid(适用Ruby)这样的库。

剩下的内容就是作者的Promoting了,你可以去原文看。