ThingsBoard 的 Actor 模型
正文与配图来自原稿(网上资料整理)。理解 TB 为何用 Actor,为下一篇「Actor 如何初始化」打基础。
0、概述
下面是从网上查阅资料后的总结。
1、背景
多线程是基本功,也是难点——各种「锁」问题很头疼。例如转账:可能有人说,对两个账户加锁、再加 synchronized 就行:

这段代码仍可能死锁:线程 1 持有账户 A 等 B,线程 2 持有 B 等 A:

除了加锁,还有别的办法吗?有,就是 Actor 模型(Actor model)。
2、Actor 模型简介
Actor 模型由 Carl Hewitt 于 1973 年提出,经 Erlang OTP 推广;消息传递更贴近面向对象「对象通信」的初衷。它属于并发组件模型,用组件定义并发范式,避免开发者直接抠多线程、线程池等细节。
Actor 是通用并发模型,不绑定某一语言:Erlang 语言级支持(如 RabbitMQ 相关生态),Akka 是 JVM 上常见实现,Spark 底层通信也曾基于 Akka 的 Actor。
基础是消息传递:一个 Actor 是基本计算单元,可收消息、基于消息计算、再给其它 Actor 发消息。Actor 之间隔离、不共享内存。
Actor 封装状态与行为;并发时主要关注消息本身。消息通常是不可变对象,从而少碰锁、内存可见性等经典坑。
由三部分组成:
| 部分 | 含义 |
|---|---|
| 状态(State) | Actor 自己的变量,自己管理,减少锁与原子性问题 |
| 行为(Behavior) | 计算逻辑;依收到的消息改变状态 |
| 邮箱(MailBox) | Actor 间通信桥梁;内部多为 FIFO 队列存消息 |

按消息流向可分发送方 / 接收方;同一 Actor 可兼两者。Actor 串行处理自己邮箱中的消息,消息不可变。
3、Actor 模型的特点
更高级的抽象
类似 OO 中的对象:每个实例封装自身状态,与其它 Actor 物理隔离。例如游戏里每个玩家是 Player Actor 的一个实例(ID、昵称、攻击力等),代码形态接近面向对象,内存里也是多个实例。
无锁
Java/C# 等要操心锁与原子性;Actor 内部状态只由自己改(通过收消息改状态),内部按单线程方式执行(类似 Redis 的单线程处理模型),适合实现分布式锁一类语义。
异步
每个 Actor 有专用 MailBox。发消息不是直接调对方方法,而是投递到对方邮箱——像邮递员把信放进信箱就走,不必等收信人当面签收:

从而解耦 Actor:大量 Actor 各自按自己的节奏跑,发/收消息不互相阻塞。
隔离
实例各自维护状态,不是「共享数据 + 锁」;通信是真正投递消息,而非共享内存上的方法调用。
天生分布式
位置透明:Actor 在本地还是远程,对调用方代码可一致。实例很轻(往往几百字节级),单机可支撑大量实例。可横向扩展;路由决定消息发往何处。
生命周期
类似有自己的生命周期;可销毁释放资源。销毁可手动或由系统策略自动化。
容错
不完全依赖处处防御式编程(如满屏判 null),而常遵循「任其崩溃」:由监督者处理崩溃——重建实例、打日志等,提高管理灵活性。
4、Actor 模型应用场景
多线程并发 / 资源竞争
执行模型偏「单 Actor 内串行 + 整体异步」,适合秒杀、转账等资源竞争场景。
转账例子:账户 A Actor、账户 B Actor,再加一个转账协调 Actor。转账 Actor 收到「转 50 元」后,分别向 A/B 发取款、存款消息;各 Actor 独立处理,不用锁:

若过程中余额不足等异常,还需事务或补偿(此处不展开)。
物联网平台
高并发、事件驱动、轻量级处理(每 GB 堆可支撑大量 Actor)很契合 IoT。开源物联网平台 ThingsBoard 即采用 Actor 做高并发设备数据处理:
- 按功能拆成多个 Actor,可互相发消息又彼此独立
- 支持多 Actor 并发;某 Actor 异常可由监督策略重启同类 Actor,降低整系统崩溃风险
据原稿引用的测试:约 4 节点(3 个 Cassandra + 1 个服务节点)集群可处理约每小时 1 亿量级数据,节点还可扩展:



其它场景还包括流式处理(如部分流框架用 Actor 做分布式通信)、定时任务等。总之 Actor 提供更高层并发抽象,让你更多关注业务逻辑。
补充(辅)
- ThingsBoard 并未直接用 Akka 作为唯一实现,而是自研了一套 TbActor 体系(见下一篇初始化)。概念仍适用:邮箱、串行处理、按实体拆 Actor。
- 下一篇:Actor 系统如何初始化。
