【Akka系列】之 Actor模型如何满足现代分布式系统的需求

网友投稿 731 2022-05-30

本文翻译自https://doc.akka.io/docs/akka/current/guide/actors-intro.html

Actor模型如何满足现代分布式系统的需求

正如前面主题叙述的一样,常见的编程实践没有很好的解决高要求的现代系统的需求。幸好,我们不需要废弃已有的编程实践。相反,Actor模型以一种原则性的方式解决了这些不足之处,使系统更加匹配我们的预期模型。Actor模型抽象使你以通信的方式思考你的代码,像一个大组织里人与人之间的交流一样。

Actors使我们:

无需借助锁实现封装。

使用响应信号、改变状态和相互发送信号的协作实体的模型驱动整个应用向前。

不再担心和我们的世界观不匹配的执行机制。

相对于调用方法,actors之间发送消息。发送一条消息并未将“执行线程”从发送者转移到目标。一个actor可以发送一条消息并无阻塞的继续运行。因此,在同样的时间内,它可以完成更多任务。

对于对象,当方法返回时,它会释放对其执行线程的控制。在这方面,actors和对象非常像,处理完当前消息后它们回复该消息并返回执行。这样,actors实际上实现了我们对“对象”设想的执行:

消息传递和方法调用之间一个重要的区别是消息没有返回值。通过发送一条消息,一个actor委派一个任务给另一个actor。正如我们在上篇《一个调用栈的错觉》部分所看到的,如果发送消息的actor希望得到一个返回值,那么它要么需要阻塞要么在同一个线程内执行另一个actor的任务。相反,接收消息的actor在一个回复消息中传递结果。

我们的模型需要的第二个关键改变是恢复封装。Actors像对象响应方法调用一样响应消息。区别在于不是多线程入侵了actor并对内部状态和不变体造成破坏,而是actors独立处理收到的消息,并且它们一个一个地响应连续到来的消息。虽然每个actor连续地处理发给它的消息,不同的actors之间并发地工作,所以一个actor系统可以同时处理硬件可支持数量的消息。

因为每个actor中最多一个消息正在被处理,一个actor无需同步即可保持不变体。这无需使用锁而自然存在:

总之,当一个Actor收到一条消息时:

Actor将这条消息添加到队列尾部

如果Actor没有被调度执行,它将被标记为ready。

一个(隐藏的)调度器实体获取这个Actor并开始执行它。

Actor在队列头部取出一条消息。

Actor改变内部状态,给相应actor发送消息。

Actor被调度完毕。

为了完成以上行为,每个Actor有:

一个邮箱 (存储收到消息的队列)

一个行为(actor的状态,内部变量等等)

消息 (代表一个信号的数据段,类似于方法调用及参数)

一个执行环境 (响应消息的actors所处的并调用actors的消息处理代码的机器)

一个地址 (more on this later)

消息存到actor的邮箱。Actor的行为描述actor如何响应消息 (比如发送更多的消息和改变状态)。一个执行环境管理一个线程池以透明地驱动这些行为。

这是一个非常简单的模型,并且它解决了上篇文章枚举的问题:

【Akka系列】之 Actor模型如何满足现代分布式系统的需求

通过将执行与信号解耦保持封装 (方法调用转移执行,消息传递没有转移)

不再需要锁。一个actor的内部状态只有通过一个一个被处理的消息修改,这消除了当试图保持不变体时的竞争。没有了锁,发送消息的actors不会被阻塞。成千上万的actors可以被高效的调度在十几个线程上以发挥现代CPUs的最大潜力。任务委派是actors操作的自然模式。

Actors的状态是本地的并且没有被共享,修改和数据通过消息传送,这映射了现代内存层次结构的真实工作方式。在许多情况下,这意味着只转移包含消息中数据的cache行而将局部状态和数据cache保持在原本的核。这个模型恰好映射了远程通信——状态被保存在机器RAM内,修改和数据作为数据包(packets)通过网络传送。

因为没有了相互发送消息的actors之间共享的调用栈,所以我们要以不同的方式处理error情况。有两种我们需要考虑的errors:

第一种情况是当目标actor被委派的任务由于任务出错而失败时 (典型的是校验问题,比如一个不存在的用户ID)。这种情况下,目标actor封装的服务是完好无损的,只是任务自身是错误的。目标actor应该回复消息发送者actor复一条消息,报告错误情况。这没什么特别的,错误是域的一部分,因此是普通信息。

第二种情况是当一个服务自身遇到一个内部错误。Akka将所有actors组织成一个树状层级结构,例如,一个actor是它所创建的新actor的父亲。这和操作系统将进程组织成一棵树十分相似。就像一个进程一样,当一个actor失败的时候,它的父actor被通知并且它可以对失败做出反应。同样地,如果父actor停止了,它的所有子actors也递归地停止。这个服务被称为监督,它是Akka的核心。

一个监督者(parent)可以决定在特定类型失败的时候重启或者在其他类型失败的时候完全停止它的子actors。子actors不会悄悄地死掉 (带有进入一个无限循环的明显异常),相反它们要么失败,它的父actor处理error,要么停止 (这种情况下相关的单元自动被通知)。总有一个负责管理一个actor的实体:它的父actor。重启在外部不可见:当目标actor重启之后,相关的actors可以继续发送消息。

现在,让我们简单看一下Akka提供的功能。

智能数据 EI企业智能 表格存储服务 CloudTable

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:【湖北鲲鹏技术公开课】干货满满,助力开发者一键获取算力提升的相关经验
下一篇:FPGA基础知识极简教程(8)详解三态缓冲器
相关文章