您的位置 首页 java

解密分布式到微服务:聊聊分布式计算,不得不说的Actor模型

聊聊分布式计算

不管是网络、内存还是存储的 分布式 ,它们的最终目标都是实现计算的分布式:数据在各个计算机节点上流动,同时各个计算机节点都能以某种方式访问共享数据,最终分布式计算后的输出结果被持久化存储和输出。分布式计算作为分布式系统里最重要的一个能力和目标,也是大数据系统的关键技术之一。经过多年的发展与演进,目前业界已经存在很多成熟的分布式计算开源编程框架和平台。作为架构师,我们应该尽可能地了解和掌握这些框架和平台。

不得不说的 Actor模型

Actor模型于1970年年初被提出,为并行计算而生,理念非常简单:所有对象皆Actor,在Actor之间仅通过发送消息进行通信,所有操作都是异步的,不同的Actor可以同时处理各自的消息,使整个系统获得大规模的并发能力。但是,该理念在当时有些超前,因此很快被人遗忘。

直到Erlang这种基于Actor 模型设计的面向并发编程的新语言横空出世,在并发领域竖起一座丰碑,Actor 模型才再次成为分布式计算领域的热点技术之一 。

目前,几乎在所有主流开发平台下都有了Actor模型的实现: Java平台下Scala的Actor类库和Jetlang; NET 平台下的MS CCR和Retlang; F#平台 下的MailboxProcessor; 微软基于MSCCR构建的新语言Axum。

Smalltalk的设计者、面向对象编程之父AlanKay曾经这样描述面向对象的本质:

很久以前,我在描述“面向对象编程”时使用了“对象”这个概念。很抱歉这个概念让许多人误入歧途,他们将学习的重心放在了“对象”这个次要的方面,而忽略了真正主要的方面一“消息”。创建一个规模宏大且可水平扩展的系统的关键,在于各个模块之间如何交流,而不在于其内部的属性和行为如何表现。

这段话也概括了Actor模型的精髓一我们可以认为Actor模型是面向对象模型在并发编程领域的一个扩展。Actor 模型精心设计了消息传输和封装的机制,强调了面向对象。我们可以将一个Actor 类比为-一个对象,对象提供了方法以供其他对象调用,等价于一个Actor可以处理某些类型的消息并进行响应,但与方法调用不同,Actor 之间的消息通信全部是异步模式,避免了同步方法调用可能产生的阻塞问题,因此很适合高度并行的系统。但是,异步编程这种思维模式大大增加了我们在编程中所耗费的脑力劳动,很难被习惯了CRUD的大众程序员们所接受,所以注定了Actor模型曲高和寡的命运。

从另一方面来考虑, Actor 模型大大简化了并行编程的复杂度。我们知道,对象一般都有属性(状态),但如何高效、安全地处理对象的可变属性,成为 多线程 并发编程领域中公认的编程难题。比如 Java 为了解决这一-难题, 先后设计了volatile 变量、Atomic 变量、基于Atomic的CAS原子计数器指令、轻型的Lock锁,最后祭出了Java 并发领域专家Doug Lea教授潜心数年编写的难度极高的并发集合框架一Java Concurrent Collection。但是即使从业多年,我们依然难以写出一-段工 业级质量的多线程代码。Actor之父一Carl Hewitt很早就敏锐地意识到了这个问题,于是定义了Actor的第2个重要特性:Actor模型的内部状态不能被其他Actor访问和改变,除非发送消息给它。那么消息是否可变呢?显而易见,Actor 发出的消息也是不可变的。

Actor模型舍弃了共享变量和共享内存这种常规编程模式,虽然失去了一定的灵活性,却让任意两个Actor都具备了跨越网络实现分布式计算的天然基因,从而成就了其在大规模分布式计算领域的“不老传说”。

如下所示是Actor模型的原理图。

从上图可以看到,每个Actor 都有一个Mailbox (邮箱),Actor A发送消息给ActorB,就好像ActorA写了-封邮件,收件人地址填写Actor B的邮箱地址,随后平台负责投递邮件。在邮件抵达Actor B的邮箱后,平台就通知Actor B收取邮件并做出回复,如果有多封邮件,则Actor B依次按顺序处理。越是简单的技术,就越有强大的力量,Actor 模型让我们再次验证了这个道理。那么,ActorB在收到消息后可能会做出哪些处理呢?

●创建其他 Actor.

●向其他 Actor发送消息。

●指定下一条消息到来时 的行为,比如修改自己的状态。

一个Actor在什么情况下会创建子Actor 呢?通常的情况是并行计算,比如我们有10个10GB的大文件要分析和处理,就可以在根Actor里创建10个子Actor, 让每个Actor都分别处理一个文件,为此根Actor给每个子Actor都发送一条Message (消息),消息里包含分配给它的文件编号(或位置),子Actor在完成处理后,就把处理好的结果封装为应答消息返回给根Actor,然后根Actor进行最后的汇总与输出。如下所示是其流程示意图。

一个Actor与其所创建的Actor形成父子关系,比如上面这个例子。在实际编程过程中,父Actor应该监督其所创建的所有子Actor的状态,原因是父Actor知道可能会出现哪些失败情况,知道如何处理它们,比如重新产生-一个 子Actor 重新做失败的任务,或者某个Actor失败后通知其他Actor终止任务。

Actor模型的优点很明显,即将消息收发、线程调度、处理竞争和同步的所有复杂逻辑都委托给了Actor框架本身,而且对应用来说是透明的。我们可以认为Actor只是-一个实现了Runnable接口的对象,在关注多线程并发问题时,只需要关注多个Actor 之间的消息流即可。此外,符合Actor模型的程序也很容易被测试,因为任意一个Actor都可以被单独进行单元测试。如果测试案例覆盖了该Actor所能响应的所有类型的消息,就可以确定该Actor的代码十分可靠。

那么,Actor 模型的缺点有哪些呢?简单总结如下。

  • Actor完全避免共享并且仅通过消息传递进行交流,使得程序员失去了精细化并发调控的能力,所以不太适合实施细粒度的并行且可能导致系统响应延时的增加。如果在Actor程序中引入一些并行框架,就可能导致系统的不确定性。
  • 尽管使用Actor模型的程序比使用 线程 与锁模型的程序更容易调试,Actor模型仍会出现死锁这一类共性问题, 也会出现- .些Actor模型独有的问题(例如信箱溢出)。

此外,Actor平台实现起来较为复杂,而且平台的性能取决于其实现原理与底层机制,比如分布式情况下的消息传输机制、网络通信机制及消息到Actor 的派发机制,在这些方面如果有处理不好的地方,就会导致整体性能和稳定性问题。比如某个Actor因为某个错误陷入死循环,疯狂地消耗CPU,基本上整个系统就瘫痪了,其他Actor很难有机会正常工作,此时Java上的 Akka 平台由于根本做不到公平调度,在出现这种问题时什么也调度不了,只能等待操作系统切换线程。而Erlang尽力实现了“可抢占的公平”调度,比较好地解决了这一难题。

本文给大家讲解的内容是架构解密从分布式到 微服务 :聊聊分布式计算,不得不说的Actor模型;

  1. 下篇文章给大家讲解的是架构解密从分布式到微服务:聊聊分布式计算, Actor原理与实践;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!

文章来源:智云一二三科技

文章标题:解密分布式到微服务:聊聊分布式计算,不得不说的Actor模型

文章地址:https://www.zhihuclub.com/172055.shtml

关于作者: 智云科技

热门文章

网站地图