2、核心组件拆解:Transport、Rule Engine、Web UI、JS Executor、Queue 服务的作用
好,咱们进入正题。上一章我们聊了 ThingsBoard 集群的整体架构,你可能觉得「嗯,大概知道怎么回事了」。但说实话,光看架构图是不够的。你得把每个组件拆开来看,搞清楚它们各自在干什么、怎么配合、以及——最重要的是——哪些地方容易出问题。
这一章,我就带你逐个过一遍 ThingsBoard 集群里的五个核心服务。我习惯把它们叫做「五虎上将」:Transport、Rule Engine、Web UI、JS Executor、Queue。每个角色都不一样,但少了谁都不行。
2.1 Transport 服务:设备的「守门员」
Transport 服务,说白了就是负责跟设备打交道的那一层。你想想看,设备可能用 MQTT、HTTP、CoAP 或者 LwM2M 协议连上来,数据格式也五花八门。Transport 的作用就是把这些乱七八糟的协议统一收口,转成 ThingsBoard 内部能理解的消息。
我个人习惯把 Transport 比作「守门员」——所有设备的数据都得先过它这一关。它负责三件事:
- 协议适配:把 MQTT 的 PUBLISH、HTTP 的 POST、CoAP 的 PUT 统统转成统一的消息格式。
- 认证与鉴权:检查设备的 Token、证书或者用户名密码,不对的直接挡在外面。
- 消息路由:把解析后的数据扔到 Queue 里,交给后面的 Rule Engine 处理。
关键点:Transport 服务是无状态的。这意味着你可以随便横向扩展,加机器就行。我在项目中遇到过客户问「为什么我的 Transport 节点 CPU 跑满了?」——多半是设备并发太高,而 Transport 实例数不够。
嗯,这里要注意:Transport 本身不处理业务逻辑。它只负责「接进来、传出去」。如果你发现 Transport 节点内存飙升,别急着调 JVM 参数,先看看是不是设备发来的消息太大,或者认证请求太频繁。
2.2 Rule Engine 服务:集群的「大脑」
Rule Engine 是 ThingsBoard 里最核心、也最容易让人头疼的组件。它负责处理所有从设备来的数据,执行规则链(Rule Chain)里的逻辑。说白了,设备数据到了 Queue 之后,Rule Engine 就把它捞出来,然后按照你配置的规则链一步步走:过滤、转换、存储、告警、触发动作……
我刚开始接触 ThingsBoard 时,总觉得 Rule Engine 就是个「消息处理器」。后来踩了坑才明白,它其实是整个系统的瓶颈所在。为什么?因为规则链里可能有复杂的脚本、外部 API 调用、甚至循环逻辑。一旦规则链写得不好,整个集群的吞吐量都会被拖垮。
Rule Engine 的几个关键职责:
- 消息处理:从 Queue 拉取消息,按规则链执行。
- 数据持久化:把处理后的数据写入 Cassandra 或 PostgreSQL。
- 告警与通知:触发告警规则,发邮件、短信或者 Webhook。
- 设备状态管理:更新设备的属性、遥测数据、时序数据。
避坑指南:我曾经遇到过一个客户,规则链里写了个死循环——每次处理完消息又给自己发一条新消息。结果 Rule Engine 节点直接 OOM,整个集群瘫痪。所以,写规则链时一定要加「消息计数」或者「超时限制」,别让逻辑把自己绕进去。
另外,Rule Engine 是有状态的。它内部维护了规则链的缓存、设备元数据、以及一些会话信息。所以扩展 Rule Engine 时,不能像 Transport 那样随便加节点——你得考虑缓存一致性和消息顺序的问题。我一般建议 Rule Engine 节点数不要超过 Queue 的分区数,否则会有节点闲着没事干。
2.3 Web UI 服务:给人类看的界面
Web UI 服务,就是咱们在浏览器里打开的那个管理界面。它负责展示仪表板、设备列表、规则链编辑器、告警记录等等。说白了,它是给运维人员和业务用户用的,不是给设备用的。
Web UI 服务本身很轻量,就是个静态资源服务器加上一些 REST API 的代理。它不直接处理设备数据,也不参与规则链执行。它的主要工作就是:
- 提供前端页面:HTML、CSS、JavaScript 文件。
- 代理 API 请求:把前端的请求转发给后端服务(比如 Rule Engine 或者 Core 服务)。
- WebSocket 推送:实时更新仪表板数据,比如设备遥测的实时曲线。
小技巧:Web UI 服务也是无状态的,可以随便扩展。但要注意 WebSocket 连接数——每个浏览器打开一个仪表板,就会建立一个 WebSocket 连接。如果用户很多,Web UI 节点的连接数会很高。我习惯在前面加一层 Nginx 做 WebSocket 负载均衡,效果不错。
你可能会问:「Web UI 挂了会影响设备数据吗?」答案是:不会。设备数据照常通过 Transport 进入 Queue,Rule Engine 照常处理。只是你看不到仪表板了。所以,Web UI 的优先级可以放低一点,但别完全不管——毕竟运维人员要靠它排查问题。
2.4 JS Executor 服务:脚本执行的「打工人」
JS Executor 这个组件,很多人容易忽略。它负责执行规则链里的 JavaScript 脚本。比如你在规则链里写了个「转换数据格式」的脚本,或者「调用外部 API」的脚本,这些脚本都是在 JS Executor 里跑的。
为什么要把 JS 脚本单独放到一个服务里?原因很简单:JavaScript 是单线程的,而且容易写出死循环或者内存泄漏。如果直接在 Rule Engine 里跑 JS 脚本,一个坏脚本就能拖垮整个 Rule Engine。所以 ThingsBoard 把 JS 执行独立出来,用沙箱隔离,跑坏了也不影响其他组件。
JS Executor 的几个特点:
- 沙箱隔离:每个脚本在独立的 GraalVM 或 Nashorn 引擎里执行,互不干扰。
- 资源限制:可以限制脚本的执行时间、内存使用量。超时直接杀掉。
- 无状态:可以横向扩展,但要注意脚本的并发执行数。
经验之谈:我曾经遇到一个场景,客户在规则链里写了个脚本,每次处理消息都要去查一次数据库。结果设备量一上来,JS Executor 的 CPU 直接飙到 100%。后来我建议他把查询结果缓存到 Redis 里,脚本只读缓存,性能提升了 10 倍。所以,写脚本时一定要考虑性能,别把 JS Executor 当数据库用。
嗯,这里还要注意:JS Executor 的节点数不是越多越好。因为每个脚本执行都需要分配一个 GraalVM 上下文,上下文切换是有开销的。我一般建议 JS Executor 的节点数跟 CPU 核心数持平,或者略少一点。
2.5 Queue 服务:消息的「高速公路」
Queue 服务,就是 ThingsBoard 集群里的消息中间件。它负责在 Transport、Rule Engine、Core 服务之间传递消息。没有 Queue,这些组件就是孤岛,谁也联系不上谁。
ThingsBoard 支持多种 Queue 实现:Kafka、RabbitMQ、AWS SQS、Azure Service Bus 等等。我个人最推荐 Kafka,因为它的吞吐量高、持久化好、而且支持分区和消费者组,非常适合集群场景。
Queue 的核心作用:
- 解耦:Transport 只管把消息扔进 Queue,Rule Engine 只管从 Queue 拉消息。两者互不依赖。
- 缓冲:当设备数据爆发式增长时,Queue 可以暂存消息,防止后端被冲垮。
- 削峰填谷:高峰期积压消息,低谷期慢慢处理,保证系统平稳运行。
- 保证顺序:同一个设备的消息可以路由到同一个分区,保证处理顺序。
重要提醒:Queue 是集群的命脉。如果 Queue 挂了,整个集群就瘫痪了。所以生产环境一定要做 Queue 的高可用。比如 Kafka 至少 3 个节点,副本因子设为 2 或 3。我曾经见过一个客户,Kafka 只部署了单节点,结果磁盘满了,所有消息都丢了……那叫一个惨。
另外,Queue 的配置也很关键。比如消息的保留时间、分区数、消费者线程数,这些参数直接影响集群的性能和稳定性。我习惯在压测时先调好这些参数,再上线。
2.6 五个组件如何协同工作?
好了,五个组件都讲完了。咱们来捋一捋它们是怎么配合的:
- 设备通过 MQTT/HTTP 发送数据到 Transport。
- Transport 验证设备身份,把数据转成统一消息,扔进 Queue。
- Rule Engine 从 Queue 拉取消息,按规则链处理。如果规则链里有 JS 脚本,就交给 JS Executor 执行。
- 处理结果(存储、告警、通知)由 Rule Engine 写入数据库或触发外部动作。
- Web UI 通过 REST API 或 WebSocket 查询数据,展示给用户。
整个过程就像一条流水线。每个组件各司其职,互不干扰。这也是 ThingsBoard 能支持百万级设备接入的原因——每个组件都可以独立扩展。
最后说一句:理解这五个组件,你就掌握了 ThingsBoard 集群的 80%。剩下的 20% 是调优和排障,咱们后面的章节会慢慢聊。下一章,我会带你手把手搭建一个 3 节点的 ThingsBoard 集群,到时候你会更直观地感受到这些组件是怎么跑起来的。