写点什么

并发编程 /Actor 模型设计为高并发项目提供基础模型(设计篇)

作者:肖哥弹架构
  • 2024-11-18
    河北
  • 本文字数:6007 字

    阅读完需:约 20 分钟

并发编程/Actor模型设计为高并发项目提供基础模型(设计篇)


Actor 模型是一种革命性的并发编程范式,它通过封装状态、行为和消息传递来构建高并发和分布式系统。自 1973 年提出以来,Actor 模型已被广泛应用于从游戏开发到金融服务等多个领域。在 Actor 模型中,每个 Actor 都是一个独立的计算实体,它们通过消息传递来交互,无需共享状态,从而避免了传统并发编程中的锁和竞态条件问题。本文将深入探讨 Actor 模型的核心概念、工作原理以及如何在实际应用中实现 Actor 模型,为读者提供一个全面的指南,以便更好地理解和利用这一强大的并发编程工具。


肖哥弹架构 跟大家“弹弹” 高并发锁, 关注公号回复 'mvcc' 获得手写数据库事务代码

欢迎 点赞,关注,评论。

关注公号 Solomon 肖哥弹架构获取更多精彩内容

历史热点文章

1、消息传递模型与区别

基于 Channel 的消息传递模型和基于 Actor 的消息传递模型在设计和使用上有不同的特点和复杂性,它们适用于不同的场景和需求。以下是两种模型的一些比较:

基于 Channel 的消息传递模型(如 Go 语言)


简单性


  • 语法简洁:Go 语言中的 Channel 使用简单,语法简洁,易于理解和使用。

  • 同步通信:Channel 自然支持同步通信,发送和接收操作都是阻塞的,这简化了并发编程的复杂性。

  • 轻量级:Channel 和 goroutine 都是轻量级的,创建和销毁的开销小,适合大量并发任务。局限性

  • 固定顺序:Channel 通信通常是线性的,数据在 Channel 中按发送顺序传递。

  • 单一通信路径:每个 Channel 只能有两个直接的通信方(发送者和接收者)。

基于 Actor 的消息传递模型(如 Akka 框架)


复杂性


  • 强大的抽象:Actor 模型提供了更高级的抽象,每个 Actor 都可以看作是一个并发执行的对象,拥有自己的状态和行为。

  • 异步通信:Actor 之间的通信是异步的,发送消息后不需要等待接收者的响应。

  • 容错和持久性:Actor 模型通常包含容错机制,如监督策略、持久化和持久化查询,这增加了模型的复杂性。优势

  • 解耦合:Actor 模型天然支持解耦合,每个 Actor 独立管理自己的状态,通过消息传递与其他 Actor 通信。

  • 可扩展性:适合构建大规模分布式系统,Actor 可以在不同的节点上运行,易于扩展。

  • 并发性:Actor 模型支持高度并发,每个 Actor 都是一个并发实体,可以同时处理多个消息。总的来说,基于 Channel 的消息传递模型在某些方面更简单,特别是在简单的并发任务和同步通信场景中。而基于 Actor 的消息传递模型提供了更多的功能和灵活性,适合构建复杂的并发系统,尤其是在需要高度解耦合和容错能力的场景中。选择哪种模型取决于具体的应用需求、系统复杂性以及开发团队的技术栈和经验。

2、Actor 并发模型介绍

Actor 并发模型是一种软件架构范式,用于构建并发和分布式系统。它基于 Actor 的概念,其中每个 Actor 是一个并发执行的实体,拥有自己的状态和行为,并通过消息传递与其他 Actor 进行通信。以下是 Actor 模型的关键特点和介绍:


  1. Actor 定义

  2. Actor 是一个并发对象,它封装了自己的状态、行为和邮件箱(Mailbox)。

  3. 消息传递

  4. Actor 之间通过发送消息来通信,消息传递是异步的,不共享内存。

  5. 无共享状态

  6. 每个 Actor 都有自己的私有状态,这减少了并发编程中的同步需求。

  7. 封装性

  8. Actor 封装了自己的状态和行为,使得系统更加模块化和易于管理。

  9. 并发性

  10. Actor 模型天然支持高并发,每个 Actor 可以独立执行,提高了系统的并发处理能力。

  11. 响应性

  12. Actor 模型强调响应性,Actor 对消息做出响应并执行相应的行为。

  13. 故障隔离

  14. Actor 模型支持故障隔离,一个 Actor 的失败不会影响到其他 Actor。

  15. 监督者-监控者模式

  16. Actor 可以监控其他 Actor(子 Actor),并在子 Actor 失败时进行恢复。

  17. 分布式系统

  18. Actor 模型适合构建分布式系统,Actor 可以分布在不同的节点上,通过网络进行通信。

  19. 可扩展性

  20. 系统可以通过增加更多的 Actor 来扩展,以适应不断增长的负载。

  21. 容错性

  22. Actor 模型提供了容错机制,如监督者可以重启失败的子 Actor。

  23. 不可变性

  24. 消息和状态通常是不可变的,这有助于简化 Actor 的行为和减少并发问题。

  25. 执行模型

  26. Actor 模型通常采用事件驱动的执行模型,Actor 对消息队列中的消息做出响应。Actor 模型的这些特性使其成为构建复杂并发和分布式系统的理想选择,尤其是在需要高吞吐量、高可靠性和可扩展性的场景中。Akka 框架是实现 Actor 模型的一个流行工具,它提供了丰富的 API 和工具来构建基于 Actor 的系统。

3、Actor 并发模型的软件架构图


有以下组件和它们之间的关系:


  1. Actor 系统

  2. 管理所有 Actor 的顶层容器,可以包含多个 Actor。

  3. Actor

  4. 系统中的基本并发单元,拥有自己的状态和行为。

  5. 消息

  6. Actor 之间通信的媒介,Actor 通过发送和接收消息来交互。

  7. 子 Actor

  8. 由现有 Actor 创建的 Actor,形成层次结构。

  9. 监督者 Actor

  10. 负责监督其他 Actor 的执行,处理子 Actor 的失败和重启。这些组件之间的关系如下:


  • Actor 系统 - 包含 - Actor

  • Actor 系统包含多个 Actor。

  • Actor - 发送/接收 - 消息

  • Actor 发送和接收消息。

  • Actor - 创建 - 子 Actor

  • Actor 可以创建子 Actor。

  • Actor - 被监督者监控 - 监督者 Actor

  • Actor 可以被监督者 Actor 监控。

  • 消息 - 被处理 - Actor

  • 消息由接收它的 Actor 处理。

  • 子 Actor - 继承自 - Actor

  • 子 Actor 继承了普通 Actor 的特性和行为。

  • 监督者 Actor - 监督 - Actor

  • 监督者 Actor 负责监督和管理普通 Actor 的生命周期。

4、Actor 内部结构图


Actor 的主要内部组件:


  • 状态(State) :Actor 维护的私有数据。

  • 行为(Behavior) :定义 Actor 如何处理消息。

  • 邮箱(Mailbox) :Actor 接收消息的队列。

  • 生命周期管理(Lifecycle Management) :管理 Actor 的创建、启动、停止和重启。

5、Actor 消息处理流程图


Actor 处理消息的流程:


  • 消息发送:Actor 异步发送消息给其他 Actor。

  • 消息接收:Actor 从邮箱中接收消息。

  • 行为执行:Actor 根据消息执行相应的行为。

  • 状态更新:行为执行可能导致状态变更。

  • 响应消息:Actor 发送响应消息给其他 Actor。

6、Actor 之间交互方式图


Actor 之间的不同交互方式:


  • 点对点通信:Actor A 直接向 Actor B 发送消息。

  • 广播通信:Actor A 向多个 Actor 广播消息。

  • 请求-响应模式:Actor A 向 Actor C 发送请求并等待响应。

  • 监督和故障处理:监督者 Actor E 监督子 Actor F 的行为,并在故障时采取行动。

7、Actor 工作模型


  1. Actor 1 发送消息

  2. Actor 1 向 Actor 2 和 Actor 3 发送消息,启动或请求某些操作。

  3. Actor 2 和 Actor 3 处理消息

  4. Actor 2 和 Actor 3 接收来自 Actor 1 的消息,并根据消息内容执行相应的操作。

  5. Actor 2 和 Actor 3 发送消息

  6. Actor 2 向 Actor 4 发送消息,可能是请求或响应。

  7. Actor 3 也向 Actor 4 发送消息,可能是不同的请求或响应。

  8. Actor 4 处理消息

  9. Actor 4 接收来自 Actor 2 和 Actor 3 的消息,并执行相应的操作。

  10. Actor 4 发送反馈

  11. Actor 4 向 Actor 1 发送反馈消息,可能是操作结果或状态更新。

  12. Actor 1 接收反馈

  13. Actor 1 接收来自 Actor 4 的反馈消息,完成整个通信过程。

8、Actor 并发模型设计初衷

Actor 模型的设计起源于并发计算的需求,特别是在多核处理器和分布式系统环境中。以下是 Actor 模型的几个关键设计因素:


  1. 并发计算的需求

  2. 随着多核处理器的出现,需要一种有效的并发计算模型来充分利用硬件资源。Actor 模型提供了一种数学模型,用于处理并发计算,其中“actors”被视为并发计算的通用原语。

  3. 消息传递机制

  4. Actor 模型采用消息传递机制来实现 Actor 之间的通信,这是为了避免传统并发程序中共享状态的复杂性问题。每个 Actor 都有自己的私有状态和行为,与其他 Actor 之间不共享任何状态信息。

  5. 封装和独立性

  6. Actor 模型中的每个 Actor 都是一个独立的计算实体,它们可以并发处理消息而不会互相干扰,这极大地降低了并发编程中的复杂性。

  7. 动态结构

  8. Actor 模型支持在运行时动态创建更多的 Actor,这种动态性使得 Actor 模型能够根据需要扩展系统。

  9. 高并发和分布式系统

  10. Actor 模型非常适合处理大规模并发的应用,如实时消息系统、分布式计算和网络游戏等。

  11. 避免共享状态的问题

  12. 传统的并发模型通常依赖于共享内存和同步机制,这可能导致数据竞争和死锁等问题。Actor 模型通过消息传递来避免这些问题,提供了一种更安全、更可靠的并发编程方式。

  13. 容错和可扩展性

  14. Actor 模型的设计理念还包括容错和可扩展性。Actor 可以监控其他 Actor 的状态,并在必要时重启失败的 Actor,从而提高系统的容错能力。

  15. 理论基础

  16. Actor 模型不仅是一种编程范式,它还具有坚实的理论基础,包括数学框架和定律,这使得它成为一种精确、完整、正式定义的理论。

9、Actor 并发模型与传统并发模型差别

Actor 模型与现有的并发计算组件的主要区别在于其独特的设计哲学和处理并发的方式。以下是几个关键点:


  1. 消息驱动

  2. Actor 模型是基于消息传递的并发模型,Actor 之间通过发送消息来通信,而不是共享内存。这与传统的并发模型(如基于线程的模型)不同,后者通常依赖于共享内存和同步机制(如锁)来管理并发访问。

  3. 封装性

  4. 在 Actor 模型中,每个 Actor 都有自己的私有状态,其他 Actor 不能直接访问这些状态,只能通过发送消息来请求服务或响应请求。这种封装性有助于避免竞态条件和死锁问题,简化并发编程。

  5. 无共享状态

  6. Actor 模型避免了传统并发编程中的共享状态问题,每个 Actor 都是一个独立的计算单元,拥有自己的状态和行为,状态不会被其他 Actor 直接访问或修改。

  7. 并发性和扩展性

  8. Actor 模型天然支持高并发和易于扩展。由于 Actor 是独立的计算单元,它们可以并行执行,并且可以根据需要动态创建更多的 Actor 来扩展系统。

  9. 容错性

  10. Actor 模型支持容错机制,如监督者(Supervisor)和监控者(Monitor)模式,可以用于实现 Actor 的监控和重启,提高系统的稳定性和可靠性。

  11. 异步性

  12. Actor 模型中的通信是异步的,发送消息后,发送者不会阻塞等待响应,可以继续执行其他任务。这种异步性提高了系统的响应性和吞吐量。

  13. 避免锁

  14. 由于 Actor 之间不共享状态,因此通常不需要使用锁来同步访问,这减少了锁竞争和死锁的风险。

  15. 适用于分布式系统

  16. Actor 模型不仅适用于单机并发编程,还非常适合构建分布式系统。Actor 可以分布在不同的节点上,通过网络进行通信,这使得构建大规模分布式系统变得更加容易。总的来说,Actor 模型提供了一种与传统并发模型不同的并发编程范式,它通过消息传递、无共享状态和异步通信来简化并发编程,提高系统的可扩展性、容错性和响应性。

10、Actor 优缺点

优点:

  1. 封装性和模块化

  2. 每个 Actor 都有自己的状态和行为,这促进了良好的封装性和模块化,使得系统更容易理解和维护。

  3. 无共享状态

  4. 由于 Actor 之间不共享状态,这减少了锁的需求,从而降低了死锁和竞态条件的风险。

  5. 并发性和可扩展性

  6. Actor 模型天然支持高并发,并且可以轻松地扩展到多核和分布式系统中。

  7. 容错性

  8. Actor 模型支持监督者-监控者模式,可以监控子 Actor 的状态,并在子 Actor 失败时重启它们,提高了系统的容错性。

  9. 异步通信

  10. Actor 之间的通信是异步的,这有助于提高性能和响应性,避免了阻塞操作。

  11. 适用于分布式系统

  12. Actor 模型适合构建分布式系统,因为它可以跨越网络在不同的节点上运行。

  13. 简化并发编程

  14. Actor 模型简化了并发编程,使得开发者可以专注于 Actor 的行为和消息处理,而不是线程管理和同步。

缺点:

  1. 性能开销

  2. 消息传递和 Actor 创建可能会带来一定的性能开销,尤其是在高负载和低延迟的场景下。

  3. 调试难度

  4. 由于 Actor 模型的非阻塞和分布式特性,调试 Actor 系统可能比传统的同步系统更复杂。

  5. 资源消耗

  6. 如果不当使用,可能会导致资源(如内存和处理器)的过度消耗,尤其是在创建大量 Actor 的情况下。

  7. 消息顺序

  8. 在某些情况下,可能需要保证消息的顺序,这在 Actor 模型中可能需要额外的工作。

  9. 不适合所有场景

  10. 对于某些类型的计算密集型任务,传统的线程和锁可能更有效。

  11. 网络延迟

  12. 在分布式 Actor 系统中,网络延迟可能成为性能瓶颈。

  13. Actor 间依赖管理

  14. 当 Actor 之间存在复杂的依赖关系时,管理这些依赖可能会变得复杂。总的来说,Actor 模型在处理高并发、分布式系统和需要良好封装的场景中表现出色,但在某些特定场景下可能需要权衡其性能开销和复杂性。选择合适的并发模型取决于具体的应用需求和上下文。

11、Actor 模型实战

业务说明

在一个分布式聊天系统中,每个用户可以发送消息给其他用户,也可以接收来自其他用户的消息。我们需要确保消息的顺序和一致性,并且系统需要能够处理大量并发用户。


以下是使用 Akka 框架代码示例,展示了如何实现一个简单的聊天系统:


import akka.actor.AbstractActor;import akka.actor.ActorRef;import akka.actor.ActorSystem;import akka.actor.Props;
// 定义消息类class ChatMessage { public final String from; public final String to; public final String content;
public ChatMessage(String from, String to, String content) { this.from = from; this.to = to; this.content = content; }}
// 定义用户Actorclass UserActor extends AbstractActor { @Override public Receive createReceive() { return receiveBuilder() .match(ChatMessage.class, this::handleChatMessage) .build(); }
private void handleChatMessage(ChatMessage message) { if (message.to.equals(getSelf().path().name())) { System.out.println("User " + message.to + " received message from " + message.from + ": " + message.content); } else { // 转发消息 getContext().actorSelection("/user/" + message.to).tell(message, getSelf()); } }}
public class ChatSystem { public static void main(String[] args) { // 创建Actor系统 ActorSystem system = ActorSystem.create("ChatSystem");
// 创建用户Actor ActorRef user1 = system.actorOf(Props.create(UserActor.class), "user1"); ActorRef user2 = system.actorOf(Props.create(UserActor.class), "user2");
// 用户1向用户2发送消息 user1.tell(new ChatMessage("user1", "user2", "Hello, user2!"), ActorRef.noSender());
// 用户2向用户1发送消息 user2.tell(new ChatMessage("user2", "user1", "Hi, user1!"), ActorRef.noSender());
// 等待一段时间,让消息处理完成 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
// 关闭Actor系统 system.terminate(); }}
复制代码


发布于: 刚刚阅读数: 3
用户头像

智慧属心窍之锁 2019-05-27 加入

擅长于通信协议、微服务架构、框架设计、消息队列、服务治理、PAAS、SAAS、ACE\ACP、大模型

评论

发布
暂无评论
并发编程/Actor模型设计为高并发项目提供基础模型(设计篇)_Java_肖哥弹架构_InfoQ写作社区