想象你走进一家生意火爆的餐厅:前厅服务员飞快地记单,后厨的炒锅师傅火力全开,传菜员端着盘子来回穿梭。奇怪的是,服务员从不会冲进后厨抢过锅铲自己炒菜——他们只是把写好的菜单放进传菜口,然后转身去服务下一桌;厨师做好菜,也只需要放到出餐口,就有传菜员取走;哪位师傅炒坏了菜,主厨会按规矩处理——能救则提醒几句,状态差就换岗调整,屡教不改就辞退。整个餐厅几百号人同时忙碌,却井然有序。这家餐厅的管理秘密,正是 Akka 中 Actor 模型的精髓:每个人都是独立的小单元,只通过“消息”协作,状态各自封装,错误由直属上级按策略处理。这篇文章,就用这家餐厅的视角,带你走进 Scala 的 Akka Actor 并发世界。
掌握 Actor 模型,你就能在设计高并发 AI 服务时摆脱锁和共享内存的噩梦,用“消息”构建出易扩展、可容错的分布式系统——这是大数据平台、实时推荐和训练调度岗位的核心竞争力。
目录
可视化演示
\u5c55\u793aAVL\u81ea\u5e73\u8861\u4e8c\u53c9\u641c\u7d22\u6811\u5728\u4f9d\u6b21\u63d2\u516510\u300120\u300130\u300140\u300150\u300125\u540e\u7684\u65cb\u8f6c\u91cd\u5e73\u8861\u8fc7\u7a0b\uff0c\u52a8\u6001\u53ef\u89c1\u5de6\u65cb\u3001\u53f3\u65cb\u53ca\u5b50\u6811\u7ed3\u6784\u53d8\u5316\u3002
本节配有交互动效(进入视口后自动加载,约 3KB)。
AVL\u6811\u5e73\u8861 \u5df2\u5e73\u8861\u8282\u70b9 \u65cb\u8f6c\u8282\u70b9 |BF|\u22641 \u81ea\u5e73\u8861\u4e8c\u53c9\u6811
1. Actor 基础概念——每位员工都是一台独立的小机器
餐厅里任何一位员工——服务员小王、主厨老张、打荷小刘——都有自己的工位和职责。老张不会跑过去替小王端菜,小王也不会冲进后厨抢锅铲。收银员只负责收银,炒锅师傅只管火候。每位员工都像一台独立的小机器:有一份“待办篮”(别人塞给他的话)、一本“工作手册”(遇到什么事该怎么反应)、一个“工号”(别人找他的唯一方式)。这家餐厅赖以运转的整体制度,就是 ActorSystem(演员系统);每一个具体岗位上的员工,就是一个 Actor(演员)。
在 Akka 里,一个 Actor 就是并发计算的最小单元。它运行时由 Akka 的调度器分配到某条线程上执行,但 Actor 本身并不等于线程。一个 Actor 由三样东西构成:邮箱(Mailbox)用来暂存收进来的消息;行为(Behavior)描述收到每条消息后如何改变自己的状态、回复哪些消息;身份(ActorRef)是其他 Actor 能找到它的唯一地址。用一个三元组来概括:
在代码里,创建一个 ActorSystem 后,通过 system.actorOf 就能得到 ActorRef。Actor 的内部状态对外完全封闭,这就是“员工只管自己的小灶台”在编程世界的映射。正因如此,每个 Actor 才能安心地单线程处理问题,而不必担心别的 Actor 来“抢东西”。
- 邮箱 M存放别人发来但还没处理的消息,类似员工工位上的待办篮,先到先处理。
- 行为 B从一个状态和一条消息出发,计算出新状态和要对外发出的回复消息。
- 身份 AActorRef 是 Actor 对外的唯一引用,外部代码不能直接触碰 Actor 的内部状态。
2. 消息传递机制——点单和传菜
服务员接到客人点单,不会自己跑进后厨抢锅炒菜,而是把写好的菜单塞进传菜口,然后立刻去服务下一桌客人。厨师做好菜后放到出餐口,服务员有空就来取。整个过程中,双方都不需要干等对方,更不用互相“催进度”。这就是餐厅里最核心的协作方式:消息传递。
Akka 中最常用的消息发送方式是 tell:发完立刻返回,不管对方收到没有、什么时候处理,这就是“即发即忘”(fire-and-forget)。如果确实需要结果,可以使用 ask 模式,它会返回一个 Future,你可以设定超时时间。消息一旦发出就不可变,因为消息要跨线程传递,如果中途被改,就会像菜单被涂改一样引发混乱。消息送达接收者的邮箱后,处理顺序由邮箱策略决定,默认是先进先出。如果消息发给了已经停止或不存在的 Actor,它会被扔进“死信”(DeadLetter)队列,就像信件寄到不存在的地址后被退回邮局。
餐厅里的“传菜口”和“待办篮”就是消息加邮箱。每个人只把自己要传递的信息放进正确的地方,剩下交给对方的节奏。这样一来,双方的节奏解耦:服务员不用等厨师,厨师不用等服务员,整个系统的并发度自然就高了。
- 入队操作tell 的效果就是把消息加入对方的邮箱集合,至于对方什么时候取,那是对方的事。
- 发送耗费趋近零tell 本身几乎不花时间,因为不需要等待对方处理完。
- 消息不可变消息发出后其内容不再变化,保证任何时刻读取都得到相同结果。
3. Actor 层次结构——餐厅的组架构
大餐厅不会让几百号人平起平坐。总经理统管前厅和后厨;后厨分总厨、热菜主管、冷菜主管;下面是一线厨师和帮厨。每个人只向自己的直属上级汇报,上级管理下级。这样一旦出问题,谁管谁一清二楚,不会出现“所有人都不知道该听谁的”的混乱局面。
Akka 里的 Actor 天然组成一棵树。父 Actor 用 context.actorOf 创建子 Actor,子 Actor 的数量和生命周期由父 Actor 管理。每个 Actor 都有一个类似文件系统路径的唯一地址,比如 /user/parent/child。ActorSystem 启动时有两个根守护者(guardian):/user 和 /system,所有业务 Actor 都挂在 /user 下面。这棵树带来两个好处:一是可以按子树做批量管理,比如停掉某个模块连同它的所有子 Actor;二是错误可以沿着树向上汇报,最后总能找到“负责人”。
Actor 的世界就是一家组织了架构的餐厅:先有人,再有部门,再有岗位。谁管谁一清二楚;出问题时,顺着树就能找到负责人。这种“分层而治”的思路,让大型并发系统的管理和错误定位都变得井井有条。
- 根节点所有用户 Actor 的起点是
/user,相当于餐厅的总管理办公室,统一管辖所有业务人员。 - 父路径Actor 在树上的父级路径,代表它的直属上级是谁。
- 完整路径在父路径后面拼上自己的名字,就得到其他 Actor 可以寻址的唯一地址。
4. 监督与容错——主厨的应急处置
主厨发现炒锅师傅把一道菜炒煳了,他不会冲过去抢过锅铲替对方把菜炒完,而是冷静按规矩处理:如果是火候小失误,提醒两句继续干活(Resume);如果师傅今天状态很差,就让他下去休息,换个顶班的人重做(Restart);如果屡教不改,直接辞退(Stop)。而且主厨只处理自己的直属下属,别的部门出了乱子,由对应的主管负责。这套逻辑,就是 Actor 的“监督”。
在 Akka 中,父 Actor 天然是子 Actor 的监督者。子 Actor 抛出异常后,不会让这条异常随便冲破系统,而是会转化为一条失败消息发送给父 Actor。父 Actor 通过定义监督策略(SupervisionStrategy),决定针对不同类型的异常采取哪种行动。常用的策略是 OneForOneStrategy:一个子 Actor 失败,只处理它自己,不连坐兄弟;另一种 AllForOneStrategy 则是其中一个失败,整个小组都重新处理。默认配置下,普通 Exception 触发重启,初始化异常触发停止。
就像餐厅靠“主厨管直属下属”的机制把问题消化在局部,Actor 的监督策略让系统错误被隔离、被分级处理。某个 Actor 崩溃不会导致整个系统瘫痪,因为它的父 Actor 在下一层兜底。这就是“让它崩溃,但有人兜底”的哲学。
- 异常分类把异常分成可恢复的轻度问题、可重启的中度问题和必须停止的重度问题。
- 查表映射根据异常类型查策略表,得到一个处理动作。
- 执行动作Resume 保留原实例继续处理,Restart 重建实例,Stop 彻底终止并回收资源。
5. 传统线程模型对比——大锅饭 vs 独立小灶
传统大厨房里,几位厨师共用一块大案板、一把菜刀。谁要用都得先拿号排队(加锁),一不小心就会吵起来(死锁),菜刀被抢来抢去还可能出错(竞态)。而 Actor 餐厅里,每位员工都有自己的小灶台和小案板,互不干扰,要协作就靠传菜口递单子——根本不存在抢工具的问题。
传统并发模型的核心矛盾在于共享内存加锁。锁粒度越细,死锁和竞态的风险越高;锁粒度越粗,并行效率越低。计算机科学家 Amdahl 早就用公式说明了这一点:即使处理器数量无限增加,程序的加速比也受限于串行部分的比例。
Actor 模型不消除串行部分,但它消除了“抢锁”这一层成本。因为消息不可变且只通过邮箱传递,Actor 在处理消息时独享自己的状态,不需要加锁。Akka 用事件驱动调度把成千上万个 Actor 压到少量线程上,线程阻塞极少,扩展能力极强。从大锅饭到独立小灶,核心哲学就一句话:把“抢”变成“递”——抢要等待、要防冲突,递则只管送出,对方自己安排时间处理。
- 并行比例 P程序里真正能同时执行的部分占总执行时间的比例,取值在 0 到 1 之间。
- 资源数量 N可以并行使用的处理器或线程数量。
- 极限加速比当 N 趋于无穷时,加速比被串行部分卡住,这是共享内存模型的天花板。
6. 集群与分布式——连锁餐厅的分店扩张
一家店生意火了,老板决定开连锁。总店先在群里公布新分店地址,分店之间互相打招呼交换信息(gossip),大家随时知道谁在线、谁掉线了。顾客无论进哪家分店,只要报上会员号,系统就能通过算法把他准确分给某一个分店的服务专员,从哪家店进的,就由哪家店服务到底。
Akka Cluster 让多个 ActorSystem 组成一个逻辑上的集群。节点之间通过 gossip 协议同步成员状态,并自动做故障检测。Cluster Sharding 把有状态的 Actor 按照实体 ID 分散到不同节点:请求到来时,路由器先算出这个实体在哪个分片、哪个节点,再把消息送过去。因为消息要跨网络,所以它必须是可序列化的,通常使用 Protocol Buffers 或 JSON 编码。集群成员有 joining、up、leaving、down 等生命周期状态,配合自动故障转移,达到高可用。
连锁餐厅靠统一会员体系和稳定的分店归属让顾客“到哪里都被认识”,Akka 的 cluster 和 sharding 也是同一个道理。它把一个 Actor 系统从单机平滑扩展到几百台机器,而业务代码几乎不需要改动——这正是分布式系统梦寐以求的“透明扩展”。
- 计算哈希对实体 ID 做一致性哈希,得到一个分布均匀的大整数,相当于把会员号映射成数字。
- 取模定位将哈希值对节点数取模,得到该实体归属的节点编号。
- 扩容影响小增加节点时,只有 1/N 比例的实体需要重新迁移,其余位置不变,保证扩展平滑。
7. 实战应用模式——餐厅的高效运营
餐厅生意火爆时,门口排号的小姐姐(路由器)把客人均匀分给各桌厨师,不让他们有的忙死有的闲死;后厨的定温定时烤箱一响(调度器),店员准时去取烤好的菜,不用一直盯着;前台看到出餐口堆满了做好的菜,就会放慢接客速度(背压),免得菜做完全凉了。这三样“餐厅运营神器”,对应 Actor 实战中的三种高频模式。
路由器(Router):把同类型工作分发给一组工作 Actor,支持轮询、随机、广播等策略,Akka 里对应 RoundRobinPool、RandomPool、ScatterGatherFirstCompletedPool。调度器(Scheduler):通过 system.scheduler.scheduleOnce 或 scheduleWithFixedDelay 定时向 Actor 发消息,比如每 5 秒做一次健康检查。背压(Backpressure):当消费者处理不过来时,让生产者放慢发送,防止邮箱无限膨胀。Akka 中可用有界邮箱配合 mailbox-push-timeout 配置,或直接使用 Akka Streams 的流式背压。
餐厅高效运转的秘诀是“分流、准点、控流量”,Actor 应用模式的本质也不过这三件事:路由器分流、调度器守时、背压控量。掌握了这些模式,你就能像经验丰富的餐厅经理一样,让并发系统在高负载下依然从容不迫。
- 观察队列长度发送前查看接收者邮箱当前的消息数量,相当于看后厨出餐口堆了几个菜。
- 水位判断将队列长度与预设最大值比较,决定这次是发还是等。
- 防止雪崩背压在系统接近极限时主动降速,避免消息积压导致内存爆炸和级联失败。
AI 应用场景
实时 AI 推理服务的会话状态管理
阶段:模型服务与推理管理
解决的问题:大模型对话服务通常要为每个用户维护一段会话上下文(历史消息、意图缓存)。传统做法用一个共享 Map 加锁管理,高并发下锁竞争严重,一旦某个会话崩溃还会“带崩”其他会话。用 Actor 模型后,每个用户会话对应一个 Actor,上下文封装在 Actor 内部,互不干扰。
具体案例:某智能客服平台用 Akka 为每个在线用户创建一个 SessionActor,用户的每轮提问通过消息 tell 发给它。SessionActor 保存对话摘要状态并调用推理服务的远程 API,拿到结果后回复给前端。用户离开时,父 Actor 统一 stop 这些子 Actor,资源被及时回收。
多 Agent 协作系统中的消息路由
阶段:Agent 编排与任务调度
解决的问题:AI Agent 系统中,多个智能体(规划 Agent、检索 Agent、执行 Agent)需要互相协作。如果让每个 Agent 直接持有其他 Agent 的内部引用,系统会变成一团乱麻。用 Actor 封装每个 Agent,Agent 之间只通过消息交互,天然实现了低耦合。
具体案例:一个 AutoGPT 风格的任务系统定义了一个 SupervisorActor 作为总控,它用路由器把子任务分发给多个 WorkerActor。每个 WorkerActor 独立调用大模型 API 处理子任务,结果通过消息回报给总控。某个 Worker 调用超时被监督策略自动 restart,其他 Worker 完全不受影响。
分布式大模型训练的任务监控
阶段:训练任务调度与容错
解决的问题:大模型训练通常横跨几十台 GPU 机器,单机脚本难以管理跨节点的状态同步和异常恢复。Actor 集群天然支持跨节点消息和故障检测,可以把每个训练 Worker 封装成一个 Actor,由监督者统一管理生命周期。
具体案例:某公司的模型训练平台用 Akka Cluster 搭建调度层,每个 GPU 节点上运行一个 TrainerActor。调度 Actor 定期通过调度器发心跳消息检查各节点状态,发现某节点掉线后立刻把它的任务分片迁移到备用节点,训练得以继续。
Python 演示
"""轻量级 Actor 模型模拟器:用队列模拟邮箱,演示消息传递的核心机制。""" import queueimport threadingimport time class Actor: """一个 Actor = 独立邮箱线程 + 行为函数""" def __init__(self, name, handler): self.name = name self.handler = handler self.mailbox = queue.Queue() self._running = True self.thread = threading.Thread(target=self._run, daemon=True) self.thread.start() def tell(self, msg): """异步发送消息:塞进对方邮箱就立即返回""" self.mailbox.put(msg) def _run(self): """后台循环:不断从邮箱取消息并交给行为函数处理""" while self._running: try: msg = self.mailbox.get(timeout=0.05) self.handler(msg) except queue.Empty: pass def stop(self): """停止 Actor 的后台线程""" self._running = False self.thread.join() def make_counter(name): """创建一个只会数数的 Actor,返回其引用""" count = 0 def handler(msg): nonlocal count if isinstance(msg, dict) and msg.get("cmd") == "inc": count += 1 print(f"[{name}] 处理 inc,累计 {count}") elif isinstance(msg, dict) and msg.get("cmd") == "query": print(f"[{name}] 当前计数: {count}") return Actor(name, handler) def main(): receiver = make_counter("收件员") for i in range(5): receiver.tell({"cmd": "inc", "seq": i}) receiver.tell({"cmd": "query"}) time.sleep(0.3) receiver.stop() if __name__ == "__main__": main()
self.mailbox = queue.Queue()每个 Actor 拥有一个独立邮箱,所有收到的消息都在这里排队。def tell(self, msg):发送消息的入口,任何外部代码只能通过这个方法“递纸条”。self.mailbox.put(msg)把消息塞进邮箱后立刻返回,发送方绝不阻塞等待。def _run(self):后台线程的事件循环,让 Actor 可以持续响应不断到来的消息。msg = self.mailbox.get(timeout=0.05)从邮箱取一条消息,没有消息时短暂等待后继续尝试。receiver.tell({"cmd": "inc", "seq": i})主线程连续异步发送多条消息,不会被阻塞。receiver.stop()优雅关闭线程,保证程序能够正常退出。
常见误区
误区我刚开始学 Actor 时,认为每个 Actor 背后都有一条独立线程,Actor 越多线程越多,系统开销会线性爆炸。
正解Actor 是逻辑上的并发单元,不是物理线程。Akka 用事件驱动调度器把成千上万个 Actor 复用在少量线程上(通常与 CPU 核心数相关)。Actor 在处理完一条消息后立即释放线程,绝不阻塞等待。这就像餐厅员工很多,但休息室工位有限,谁有活谁去操作台,而不是每人一个专属工位。
为何常见:因为“每个 Actor 独立运行”太容易让人联想到“每条线程独立运行”,加上教材常强调 Actor 是“轻量级并发原语”,初学者很容易把两者画等号。
误区我以为向 Actor 发送消息就等价于调用它的一个方法,发完就能拿到返回值,甚至能在同一条调用栈里看到对方执行完的报错。
正解tell 是纯异步的 fire-and-forget:消息进入对方邮箱就返回,没有返回值。想要结果必须用 ask 返回 Future,或等对方处理完主动 reply。函数调用像你当场问同事要答案并一直站着等他回答;tell 像你把菜单放进传菜口就走——答案写在纸条上,稍后别人会传回来。
为何常见:绝大多数编程课都是从“调用→返回”的思维方式起步,从命令式思维转向消息驱动思维需要一个重新学习的过程。
误区我以为父 Actor 能像 try-catch 一样,在子 Actor 抛出异常的那一刻立刻接管,恢复现场继续执行。
正解Actor 的失败处理是隔离的。子 Actor 异常后,信使把失败消息发给父 Actor,父 Actor 按监督策略做决策。父 Actor 不能“穿越回现场”接住异常继续执行,只能三选一:resume 继续用当前实例、restart 重建实例、stop 终止。就像主厨听到帮厨打翻盘子,不能回到事故发生前接住盘子,只能决定:骂两句继续、换个人、或让这个人走。
为何常见:面向对象里的 try-catch 思维根深蒂固,而 Actor 的哲学是“让它死,有人负责处理”,而不是“在出错的地方接着跑”。
误区听说 Actor 能解决并发问题,我以为换成 Akka 程序就会在单机性能上突飞猛进,比手写线程快很多倍。
正解Actor 的核心优势是可扩展性和并发逻辑的简化,而不是单机绝对性能碾压。如果你的业务几乎没有共享状态,手写线程也能很快。但若每天被锁竞争和竞态 bug 折磨,Actor 在开发效率、可维护性和分布式能力上的收益远超那点性能差异。流水线分工不一定比一个人闷头干活快,但能轻松应对大订单和规模扩张。
为何常见:性能是新手最直观的衡量标准,而框架宣传又总喜欢用“高性能”做卖点,导致大家忽略了 Actor 真正的价值在于“让并发变得可控”。
误区我以为只要引入 Akka Cluster,随便往哪个 ActorRef 发消息,系统都会自动把消息送到另一台机器。
正解跨节点消息确实可行,但需要先满足一系列条件:消息必须可序列化;ActorRef 需要被共享(通常通过识别机制或集群路由查找);系统要靠集群分片等机制知道目标 Actor 在哪个节点。默认情况下,一个 ActorSystem 内的 ActorRef 只在本进程内有效。就像连锁餐厅的会员卡不能直接指挥别家分店的厨师——你必须通过总部信息系统下单,再由总部转发过去。
为何常见:教程常简单说“Cluster 让 Actor 系统分布式化”,省略了部署、序列化和寻址的具体前提,读者就很容易高估框架的“自动能力”。
面试题
Q1. 在 Akka Actor 模型中,Actor 之间通信的唯一正确方式是?
- A.直接调用对方的方法
- B.向对方的 ActorRef 发送消息← 正确答案
- C.修改对方 Actor 内部的状态变量
- D.通过共享的全局内存交换数据
答案:B。Actor 的核心封装原则是“状态对外不可见”,所有协作只能通过向 ActorRef 发送消息来完成。A、C、D 都是在直接操作共享状态,这正是 Actor 模型要避免的“抢案板”式并发。选 B 才符合“传菜口递单”的协作方式。
Q2. 使用 tell 方法发送消息后,发送者接下来会做什么?
- A.阻塞等待接收者返回结果
- B.立即返回,继续执行自己的任务← 正确答案
- C.等待接收者确认消息送达
- D.若对方忙则自动重发
答案:B。tell 是 fire-and-forget 的异步发送,消息一旦被放入邮箱就立即返回,发送者不关心对方何时处理、处理结果如何。这就像服务员把菜单放进传菜口就去服务下一桌。如果需要结果,应该使用 ask 模式或等待对方的 reply 消息。
Q3. 当子 Actor 抛出异常时,由谁来决定如何处理这个错误?
- A.子 Actor 自己用 try-catch 捕获并恢复
- B.子 Actor 的父 Actor 根据监督策略决策← 正确答案
- C.ActorSystem 的全局配置统一处理
- D.抛出异常时所在的线程来处理
答案:B。Akka 中父子关系天然是监督关系,子 Actor 异常会变成一条失败消息发送给父 Actor,父 Actor 按监督策略选择 resume、restart 或 stop。这对应“主厨管直属下属”的模式。A 是 OOP 思维,C 和 D 在 Akka 中都不存在这样的机制。
Q4. 以下哪些是 Akka 监督策略中允许采取的动作?
- A.Resume(恢复当前实例)← 正确答案
- B.Restart(重启 Actor 实例)← 正确答案
- C.Stop(彻底停止 Actor)← 正确答案
- D.ShutdownAll(关闭整个 ActorSystem)
答案:ABC。Akka 监督策略有三个标准动作:Resume 忽略异常继续处理、Restart 销毁旧实例创建新实例、Stop 终止该 Actor。D 中的 ShutdownAll 并不是监督策略的动作,监督只针对失败的子 Actor,不会连整个系统都关闭,除非你故意在策略里调用 system.terminate。
Q5. 与传统共享内存线程模型相比,Actor 模型的核心优势是什么?
- A.完全消灭了程序中的性能损耗
- B.通过消息传递避免了共享内存的锁竞争← 正确答案
- C.不需要管理消息队列
- D.不需要进程或线程的概念
答案:B。Actor 模型的核心思想是“共享消息,不共享状态”,每个 Actor 独享内部状态,通信靠不可变消息通过邮箱传递,因此无需加锁保护共享数据。A 夸大了效果,任何并发模型都有开销;C 错,邮箱队列正是 Actor 的核心组件;D 错,Actor 仍运行在线程之上。
Q6. 关于 Akka Cluster,下列说法正确的有哪些?
- A.节点之间通过 gossip 协议维护成员关系← 正确答案
- B.集群具备自动故障检测能力← 正确答案
- C.Cluster Sharding 可以把实体按 ID 分片到不同节点← 正确答案
- D.所有消息在集群中传输都会被自动去重
答案:ABC。Akka Cluster 用 gossip 同步成员状态,用故障检测器发现掉线节点,Cluster Sharding 通过取模等方式把实体分布到节点。D 错:Akka 默认不提供消息去重,跨节点消息的可靠性是“至少一次”或“至多一次”由应用层保证。这就像连锁餐厅的信息系统能同步分店状态,但不保证每张外卖单只被打印一次。
Q7. Actor 中的邮箱(Mailbox)起到的主要作用是什么?
- A.存储 Actor 的内部业务状态
- B.缓冲接收到的消息,实现异步解耦← 正确答案
- C.保存所有已发送消息的备份
- D.记录 Actor 的运行日志
答案:B。邮箱是 Actor 的消息缓冲区,发送方把消息放进去后立即返回,接收方按自己的节奏取出处理。这种缓冲机制让生产者和消费者的节奏解耦,对应餐厅的“出餐口”。A 是 Actor 的状态,C 和 D 都不是邮箱的职责。
Q8. 关于监督策略中的 Restart 动作,下列描述正确的有哪些?
- A.销毁旧的 Actor 实例并创建一个新的实例← 正确答案
- B.外部持有的 ActorRef 保持不变← 正确答案
- C.ActorRef 的路径会发生变化
- D.该 Actor 的兄弟 Actor 也会被重启
答案:AB。Restart 会销毁失败实例并新建实例,但 ActorRef 作为对外地址保持不变,因此调用方无感知。C 说反了,路径和引用都不变。D 是 AllForOneStrategy 的行为而非 Restart 本身的属性——默认的 OneForOne 策略不会连坐兄弟 Actor,就像换一个炒锅师傅并不会把整个后厨都换掉。