第4章:并发模型与Akka

Akka 是 Scala 生态中最成熟的并发与分布式框架,面试官通过本章考察你对 Actor 模型的理解、消息传递的线程安全、监督树容错机制以及实际项目中的调优能力。本章结构化拆解高频试题与项目案例,帮助你在 30 分钟内掌握核心回答框架。

复习重点: Actor 模型 vs 共享内存、tell/ask 区别、监督策略、Dead Letter、Dispatcher 配置。建议手动画一遍 Actor 生命周期图。
Actor 并发模型 1. 消息传递 vs 共享内存 2. tell vs ask 语义 3. 监督与容错策略 4. 性能调优要点 面试官追问路径图 简述Actor模型核心思想 如何保证消息有序性? tell和ask性能差异? 监督策略OneForOne? Dead Letter如何处理?

面试问答卡片

面试问题:Akka 中 Actor 模型与共享内存并发模型的本质区别是什么?

面试官考察点是否理解消息驱动 vs 共享状态、线程安全来源、无锁编程思想
回答框架
  1. 指出核心差异:消息传递 vs 变量共享
  2. 说明 Actor 封装状态,外部只能通过不可变消息通信
  3. 强调无共享状态避免了锁竞争
  4. 举例:邮件收发 vs 多人同时修改白板
示例回答 Actor 模型每个实例拥有私有状态,外部只能通过不可变消息与其交互;内部串行处理消息,天然避免竞态条件。例如订单 Actor 只接收“创建/支付”消息,无需锁即可保证状态一致性。
可能追问“消息有序性如何保证?” “如果 Actor 处理消息速度慢于发送,如何背压?”
避坑提醒不要陷入“Actor 完全不需要锁”的误区 – 跨 Actor 查询可能需要 Future 和回调,仍要注意线程安全。

面试问题:tell 和 ask 的适用场景及性能差异?

面试官考察点是否区分异步/同步通信、消息丢失处理、Future 开销
回答框架
  1. tell:fire-and-forget,无返回值,性能最高
  2. ask:返回 Future,内部创建临时 Actor,额外开销
  3. 选型:日志/监控用 tell;需要 response 用 ask
  4. 提示:大量 ask 会导致 Actor 系统负载上升
示例回答 在实时推荐项目中,对用户行为日志使用 tell 记录,避免阻塞;当需要查询用户画像时使用 ask,设置超时 3s,配合 recover 降级兜底。
可能追问“ask 的超时如何设置?” “ask 内部临时 Actor 的生命周期如何管理?”
避坑提醒不要在 ask 的 Future 内直接访问 Actor 内部可变状态,容易引发线程安全问题。

面试问题:Akka 的监督策略有哪些?如何选择?

面试官考察点容错设计思路、监督树层次、重启/停止/升级策略
回答框架
  1. 列举三种:OneForOne、AllForOne、自定义
  2. 说明指令:Resume、Restart、Stop、Escalate
  3. 根据子 Actor 重要性选择:非关键用 Resume,关键用 Restart
  4. 整个层次用 AllForOne 当互相依赖时
示例回答 支付系统中,交易 Actor 失败使用 Restart 重置状态,但日志 Actor 失败仅 Resume;如果数据库连接 Actor 崩溃,使用 AllForOne 停掉所有相关 Actor 并向上 Escalate 报警。
可能追问“Restart 时 Actor 的 preRestart 和 postRestart 如何用?” “Escalate 后父 Actor 如何处理?”
避坑提醒不要对临时异常启用 Stop,会导致消息丢失;尽量使用 Restart 保留邮箱。

项目案例模板:实时风控规则引擎

项目名称实时风控规则引擎(基于 Akka Cluster)
业务背景为电商平台提供毫秒级交易风控检测,根据用户行为、设备指纹、历史订单等实时决策是否拦截交易。
规模指标
  • QPS:峰值 8 万条/秒
  • 规则数:300+ 条
  • 集群节点:12 台
  • P99 延迟:<50ms
技术栈Scala 2.13、Akka Cluster / Persistence、Akka HTTP、Redis、Kafka、Prometheus + Grafana
核心难点
  • 大量规则匹配导致 Actor 邮箱积压
  • 规则热更新需要不中断服务
  • 分布式下一致性哈希路由倾斜
面试高频追问
  • 如何设计 Actor 层次?(安全:Router Actor → RuleWorker Actor → Persistence Actor)
  • 规则热更新如何实现?(使用 Akka Cluster PubSub 广播新规则,Worker Actor 通过成为 FSM 切换状态)
  • 邮箱积压如何处理?(使用 Dispatcher 配置 throughput,结合 BoundedMailbox 和背压)
  • 如何保证不丢消息?(使用 Akka Persistence 持久化事件,重启后恢复)
可迁移回答话术 “我在风控项目中采用了层级监督+FSM状态机设计:父Actor负责路由,子Actor执行规则匹配;热更新通过集群PubSub广播新版FSM配置,每个Worker在安全点切换状态,延迟控制在10ms内。”

大厂模拟面试

面试官:“你在项目中使用 Akka Cluster 时,如何解决跨节点 Actor 的消息可靠投递问题?”

候选人回答:

普通答案:“我们使用了 Akka Cluster 自带的远程通信机制,设置一定的重试次数,消息就不会丢。”

❌ 缺乏具体实现和边界情况分析。

优秀答案:“我们采用 Akka Cluster Sharding 搭配 At-Least-Once Delivery 模式。发送端使用带有确认的重试,接收端通过幂等性去重,业务层用 Akka Persistence 记录发送状态。同时设置了死信监控告警,发现丢失立即补偿。”
✅ 提到关键组件和去重保障。

专家答案:“我们分层设计:底层使用 Akka Cluster Sharding 保证消息路由到正确的 Actor;中间层封装了 Reliable Delivery 模块,基于事件溯源记录每个消息的 deliveryId,接收方确认后移除;超时未确认的重新投递,但业务侧必须幂等(以订单号+事件类型作为唯一键)。对于实时性要求高的场景(如支付),还引入了 Kafka 作为两者之间的缓冲,Akka 只处理状态机逻辑,Kafka 保证持久化与顺序。”
💡 给出分层架构 + 幂等 + Kafka 兜底,体现系统设计深度。

面试官追问:“这种方案里,如何保证消息顺序?如果重试时顺序错乱导致业务异常如何处理?”

专家回答思路:“使用同一个 Cluster Sharding 的消息单线程 Actor 保证顺序;如果重试消息到达时顺序错乱,Actor 内维护 version 或 sequence number 校验,乱序消息放入暂存队列等待补序,超时后视为异常并触发补偿事务。”

训练建议:在面试前用纸笔画出 Akka Actor 生命周期、监督树分层和 Reliable Delivery 流程图,能极大提升现场表达能力。