☰
macOS上Thingsboard源码编译与启动全流程实战
2026/9/28 18:43:57 网站建设 项目流程

搞过物联网的人应该都听过Thingsboard这个名字。作为一个开源物联网平台,它自带设备接入、数据可视化、规则引擎、多租户管理这一整套能力,生产环境里很多行业项目都是拿它直接改的。不过绝大多数人用的是官方打包好的安装包或者docker镜像,真正从源代码开始编译并跑起来的人并不多。最近我在macOS上一路从拉代码到构建再到启动,把整套流程完整走了一遍,踩了不少坑,也把一些关键细节摸清了。这篇就把Thingsboard物联网平台源代码编译与启动的过程记录下来,主要面向想在macOS上本地编译调试源码的同学,无论你是想二次开发还是单纯想研究它的工程结构,这份操作记录都能帮你避开前期的一些弯路。

1. 环境准备与源码获取

1.1 macOS本机基础环境清单

Thingsboard是个典型的Java大型项目,依赖的东西比较多。在macOS上动手之前,先把本机环境理清楚。

  • JDK:官方当前主流版本要求Java 17以上,建议直接装17,配好JAVA_HOME。
  • Maven:3.6.3以上,推荐3.8.x或3.9.x, IDE里自带的Maven也行,但建议用独立安装版,好排查问题。
  • Node.js:前端ui-ngx模块是Angular项目,需要Node.js 18或20,建议20 LTS版本。
  • PostgreSQL:建议12以上,我用的14。Thingsboard默认用PostgreSQL存储元数据和业务数据。
  • RabbitMQ:如果打算用默认的队列规则引擎,需要先跑起来,或者后续用内存队列跳过。
  • Git:这个不用多说,拉代码必备。

如果你之前机器上已经装过别的JDK版本,建议把17设为默认,避免后面编译时出现class文件版本报错。检查方式就是终端里执行java -version,确认输出的是17.x。

1.2 从官方仓库获取源代码

Thingsboard的源码托管在官方GitHub仓库,直接git clone即可。一般建议clone稳定分支,比如3.x版本的release分支,或者你想研究新特性就去main分支,但编译层面main分支有时候依赖的前端版本比较新,构建时间也会更长。我这次用的是3.6.x稳定分支。

git clone -b release-3.6 https://github.com/thingsboard/thingsboard.git cd thingsboard

注意仓库体积比较大,里面带着完整的前端工程和大量资源文件,clone过程耐心等一会儿。如果中途断了,可以用git fetch --tags继续补全。源码目录结构其实是分模块的,核心有这么几个:

  • application:主服务模块,包含启动类、REST API、规则引擎逻辑,编译后生成的东西就是最终在跑的物联网平台服务。
  • ui-ngx:前端工程,Angular写的,本地运行的主界面和仪表盘都在这里。
  • dao:数据访问层,封装了数据库、Redis等存储操作。
  • common:公共模块,各种工具类、消息队列客户端封装。
  • transport:设备接入传输层,支持MQTT、HTTP、CoAP、LwM2M等协议。

这里要说一句,很多人第一次看这个项目会懵,因为模块特别多。其实不用全看懂,编译和启动阶段只需重点关注application和ui-ngx就够了,其它模块是它们的依赖。

1.3 两个容易忽略的前置服务

源码启动时两个基础服务不能缺:PostgreSQL和RabbitMQ。这两个服务起不来,Thingsboard根本没法正常初始化。macOS上一站式安装可以用Docker,但如果你不想为编译项目再开一套容器环境,用Homebrew直接装PostgreSQL和RabbitMQ也很快。

PostgreSQL我用Homebrew装的:

brew install postgresql@14 brew services start postgresql@14

装完后默认数据库叫postgres,用户是当前macOS用户名。这时候还需要手动创建一个专门给Thingsboard用的数据库和用户,后面配置要用到。RabbitMQ的安装类似:

brew install rabbitmq export PATH="$PATH:/opt/homebrew/opt/rabbitmq/sbin" rabbitmq-server -detached

或者用brew services start rabbitmq。这里有个细节,Thingsboard默认配置会尝试连localhost:5672,如果RabbitMQ没启动,启动日志会不停刷重连错误,但不会马上挂掉,很多人被这个误导,以为服务正常运行,实际规则引擎消息根本处理不了。

2. 核心配置与编译构建原理

2.1 为什么选择源码编译而不是直接用安装包

这里先讲清楚一个概念。Thingsboard官方提供了很多分发方式,包括deb安装包、docker镜像、windows安装程序。那为什么还要源码编译?

第一个原因是二次开发需要。物联网平台项目做久了就会发现,开箱即用的平台功能往往不能完全贴合业务场景,设备接入协议可能要定制、规则引擎节点要自己写、前端仪表盘组件要单独扩展,这些都必须直接改源码再重新构建。第二个原因是调试便利,源码工程可以直接在IDE里跑,断点调试规则引擎和设备接入逻辑,比黑盒排查不知道高效多少倍。

第三个原因也很实际,官方预编译包对macOS的适配并不完整,macOS上最顺滑的方式就是源码构建后直接跑Application主类。很多同事在macOS上装官方安装包体验很别扭,而源码编译反而是最省心的路径,只是首次构建比较耗时。

2.2 配置文件结构解析

源码里最关键的两个配置文件都放在application/src/main/resources目录下:

  • thingsboard.yml:主配置,涵盖服务端口、数据库连接、消息队列、缓存、租户等。
  • thingsboard.conf:JVM启动参数和环境变量配置,本地调试时主要靠它调整内存。

打开thingsboard.yml,重点看几块。数据库相关配置在spring.datasource节点下,主要包括url、username、password。队列相关配置在queue节点下,这里可以选择in-memory或rabbitmq等实现。还有一个容易被忽略的是transport节点,它配置了MQTT、HTTP、CoAP等设备接入端口,默认值一般不改,但如果本机端口冲突就得来这里调整。

举个例子,开发环境数据库配置最简单的方式是:

spring: datasource: url: jdbc:postgresql://localhost:5432/thingsboard username: postgres password: postgres

如果PostgreSQL账号密码跟本地环境不一致,记得改成自己的。另外要确保PostgreSQL开监听localhost,Thingsboard默认不会去连Unix socket。

2.3 Maven多模块构建原理

Thingsboard用Maven管理整个Java后端工程。整个项目是一个多模块聚合工程,根目录的pom.xml统一管理所有模块的版本和依赖。构建时Maven会按依赖顺序依次编译common、dao、transport等模块,最后打的包在application/target里。

构建命令非常简单,在源码根目录执行:

mvn clean install -DskipTests

这里解释一下各个参数含义。

  • clean:清掉之前的构建产物,避免旧class干扰。
  • install:把每个模块构建到本地仓库,后续模块依赖本地包。
  • -DskipTests:跳过测试。Thingsboard项目有大量集成测试和单元测试,不跳过的话构建时间会翻几倍,而且测试环境不完整很容易失败,所以本地编译时这个参数几乎必带。

第一次构建时Maven会下载大量依赖,如果你的网络环境不太好,这一步会非常痛苦。不过macOS上一般还好,耐心等就行。完整构建时间取决于机器性能,M系列芯片大概在15到30分钟,Intel芯片会久一些。

2.4 前端构建与资源拷贝

Thingsboard的后端服务和前端是两个独立工程。后端编译完成后,还需要把前端的构建产物拷贝到后端资源目录里,否则启动后访问界面会找不到静态文件。

前端构建在ui-ngx目录下执行。先安装依赖:

cd ui-ngx npm install

这一步同样耗时较长,node_modules动辄上千MB。安装完成后构建生产包:

npm run build:prod

也可以简化为ng build --prod。构建完成后会在ui-ngx/target下生成一堆静态文件,然后需要把这些文件拷贝到后端模块的静态资源目录。很多教程会直接说mvn install会把前端也带上,但实际如果你先构建后端再单独构建前端,就必须手动拷贝:

cp -r ui-ngx/target/* application/src/main/resources/static/

如果你用的是IntelliJ IDEA,也可以用它的Maven面板直接执行mvn install,再在ui-ngx模块里用npm脚本构建前端。整体来说,后端和前端分开构建更灵活,至少前端改动后不需要重新编译整个Java工程。

3. 编译过程全记录与启动实战

3.1 首次完整编译实操记录

我这次是在一台M2芯片的MacBook Pro上操作的,系统是macOS Sonoma。具体执行过程如下。

先验证环境:

java -version mvn -version node -v npm -v

确认版本符合要求后,开始全量编译:

mvn clean install -DskipTests

执行后Maven会先解析插件,然后开始下载依赖。这里有个体验很糟糕但也特别常见的现象:终端里滚动刷屏非常快,中途看起来像卡住了,其实是在下载某个大依赖。我建议加上-T 4参数尝试多线程构建:

mvn clean install -DskipTests -T 4

实测多线程确实能压缩一些时间,但某些模块之间依赖关系比较紧密,收益不算特别明显。编译过程中最容易出现的是两三类错误:

  • 依赖下载超时,网络问题,重试几次基本能过。
  • Node前端依赖的npm源访问慢,建议在ui-ngx目录下配置.npmrc使用国内镜像源,不过这个看个人网络情况。
  • 测试类编译错误,通常是某些测试代码需要额外依赖,如果只是为了跑主服务,直接-DskipTests绕过。

编译完成后,application/target目录下会生成一个约几百MB的目录,里面有lib、conf、extensions等子目录,这就是一个可以独立运行的Thingsboard服务目录。

3.2 在IntelliJ IDEA里配置启动

命令行跑起来比较生硬,我还是推荐用IntelliJ IDEA直接调试。用IDEA打开根目录后,等待Maven导入完成。找到主启动类:

application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java

右键这个类,选择Run即可。但直接这么跑大概率会失败,原因在于IDEA默认的Working directory不对,配置文件路径和日志路径都会错位。

正确做法是在Run Configuration里设置Working directory为application模块根目录,让进程从application目录读取资源和生成日志。在VM options里建议加上:

-Dlogging.config=application/src/main/resources/logback.xml -Dthingsboard.conf=application/src/main/resources/thingsboard.conf

这里thingsboard.conf是JVM启动参数的入口文件,里面包含了初始内存和一堆环境变量设置。如果不指定,服务也能起来,但可能会因为找不到配置路径导致日志输出目录异常。

启动类跑起来后,控制台会开始打日志。正常情况下一两分钟内就会看到Started ThingsboardServerApplication,然后访问http://localhost:8080,用系统默认账号sysadmin@thingsboard.org/sysadmin登录。

3.3 系统初始化流程解析

Thingsboard首次启动时不只是启动一个Web服务,它还会做系统初始化。这个初始化过程值得了解一下,因为很多人会在这里遇到隐藏问题。

初始化主要包括这几件事:

  • 创建数据库表结构,执行Liquibase迁移脚本。
  • 创建系统管理员账号、默认租户、默认客户。
  • 初始化规则引擎相关配置。
  • 安装默认仪表盘和部件库。

所以首次启动明显比后续启动慢,这是正常现象。如果数据库里已经有旧数据,启动时Liquibase会做增量升级,中途如果中断或者数据库账号没权限,就会出现启动失败。

数据库初始化阶段最容易遇到的报错是WeatherData相关的脚本执行失败,实际上都是PostgreSQL权限问题或扩展缺失,比如项目需要建uuid-ossp扩展,而你的数据库用户不是超级用户时就可能失败。解决办法是给用户加权限:

ALTER USER youruser WITH SUPERUSER;

或者手动创建扩展:

CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "pgcrypto";

3.4 配置RabbitMQ队列模式

默认情况下Thingsboard会尝试使用内存队列,但这只适合功能演示。稍微真实一点的项目都会切到RabbitMQ模式,让规则引擎异步消费消息。配置在thingsboard.yml的queue节点:

queue: type: rabbitmq rabbitmq: exchange-name: tb_exchange host: localhost port: 5672 virtual-host: / username: guest password: guest

这里注意,RabbitMQ的guest账号默认只能在localhost访问,本地调试完全没问题。切换后需要重启服务,启动日志里会出现RabbitMqQueueAdmin之类的字眼,说明已经把队列交换机声明好了。用RabbitMQ管理界面能看到tb_exchange这个交换机,以及一堆tb_rule_engine开头的队列。

3.5 前端启动的另一种方式

生产模式是后端把前端静态文件一起服务,但如果你在开发前端功能,每次都跑npm run build:prod然后拷贝资源就太慢了。更高效的方式是前端单独起一个开发服务器。

在ui-ngx目录执行:

npm start

默认会在http://localhost:4200起一个带热更新的开发服务器,然后通过代理把API请求转发到http://localhost:8080。这样改前端代码会实时刷新,极大提升页面调试效率。

这个方案的前提是后端服务已经在8080端口跑起来了。在ui-ngx/src目录里有个proxy.conf.json之类的代理配置,里面默认就是指向localhost:8080,一般不用改。

4. 常见启动问题与排查实战

4.1 启动失败问题速查表

我把这段时间遇到和同事常问的问题汇总成了表格,方便对照排查。

现象可能原因处理思路
启动日志报数据库连接失败PostgreSQL未启动或账号密码错误先确认brew services list里postgresql状态,再检查thingsboard.yml里的url和账号
启动时一直刷RabbitMQ连接失败RabbitMQ服务未启动或配置端口不对启动RabbitMQ,检查5672端口是否被占用
访问前端界面空白前端构建产物没拷贝到static目录重新构建ui-ngx并拷贝target下的文件到后端static目录
Maven编译失败,提示找不到依赖本地仓库依赖损坏或网络下载失败删除~/.m2/repository中对应依赖目录,重新mvn install
启动时端口被占用8080端口已有其他进程用lsof -i :8080找出进程并处理,或改server.port
Application无法启动,ClassNotFoundJDK版本不匹配确认使用JDK17,检查IDEA的Project SDK设置
登录后看不到默认仪表盘数据库初始化未彻底完成检查日志是否有Liquibase错误,确认使用全新数据库再重启一次

4.2 数据库配置最容易踩的坑

IoT平台项目和普通Web项目在数据库使用上有个显著区别:Thingsboard首次启动会对数据库结构做完整初始化,而且要求当前用户有创建扩展的权限。很多人直接在电脑上装了PostgreSQL,但用的是系统默认超级用户,然后自己手贱创建了一个普通用户用来连接,结果启动时总报错。

这个坑的排查思路很简单,把数据库连接账号临时提升为超级用户,或者直接在默认超级用户下创建数据库:

psql -d postgres -c "CREATE DATABASE thingsboard;" psql -d thingsboard -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";"

配置里用postgres用户直接连也行,反正本地开发环境没什么安全顾虑。

4.3 构建超时与依赖下载缓慢的应对

在macOS上构建Thingsboard最大的敌人不是代码报错,而是依赖下载。尤其是后端Maven依赖和前端npm依赖,总量非常大。如果某次下载中断,后面编译就可能报一堆奇怪的找不到符号错误,这时候先别怀疑代码,先想想是不是本地依赖仓库缺了东西。

稳妥的办法是:

  • Maven用阿里云镜像或华为云镜像,改~/.m2/settings.xml里的mirror节点。
  • npm设置镜像源,在ui-ngx目录下新建.npmrc,内容写上registry=https://registry.npmmirror.com。
  • 下载大文件期间尽量保持网络稳定,不要中途断网。

依赖补齐之后,大部分编译问题都会自动消失。

4.4 日志定位的实用技巧

启动排查离不开日志。Thingsboard的日志默认写到哪里,其实跟启动方式有关。在IDEA里运行时,日志会直接打到IDEA的Console窗口,同时文件日志会写到application/logs目录下,比如thingsboard.log。

定位问题时我习惯先看thingsboard.log,因为它比控制台日志更完整。常见操作是:

tail -f application/logs/thingsboard.log

如果启动出现问题,先搜ERROR关键字,再往前看几行上下文。很多时候错误原因不是表面那一行,比如启动失败是因为数据库迁移失败,但报错其实是某张表的字段类型不支持,这时候就需要去翻更早的WARN日志。

另外要留意macOS上文件句柄限制。如果启动时出现Too many open files,说明系统单进程文件数上限太小。Thingsboard启动后会创建大量Netty连接和线程,默认上限可能不够。可以用ulimit -n 10240临时调大,或者直接写成启动脚本里的前置命令。

5. 编译后功能验证与日常开发建议

5.1 用设备模拟器验证平台链路

服务启动成功不代表整个平台所有链路都OK。我一直建议编译启动完成后,立刻做一次设备接入和数据可视化验证,这样可以确认消息队列、规则引擎、数据库、前端仪表盘是一条通路的。

最简单的验证方式是使用MQTT客户端连接Thingsboard的默认MQTT端口1883,发布一条模拟数据。Thingsboard允许没有凭证的设备通过provision流程注册,但更简单的是直接用默认的A1_TEST_TOKEN之类的访问令牌。实际操作中我一般先登录平台,在设备列表里创建一个新设备,系统会生成一个Access Token,然后MQTT客户端用这个Token连接并发布数据。

mosquitto_pub -d -q 1 \ -h localhost -p 1883 \ -t "v1/devices/me/telemetry" \ -u "YOUR_ACCESS_TOKEN" \ -m "{\"temperature\": 36.5, \"humidity\": 70}"

发布完数据后,回到Thingsboard界面打开设备详情,能看到最新遥测值已经显示出来了。再配合仪表盘拖一个图表部件,数据曲线马上就能出来。走到这一步,说明整个编译和启动流程已经彻底打通。

5.2 本地二次开发的模块划分建议

如果你接下来要在源码基础上做二次开发,我的建议是尽量不改核心模块的包名和结构,而是按官方提供的扩展点来加东西。

  • 设备接入自定义:优先看transport/mqtt模块的处理器逻辑,加自定义Topic解析。
  • 规则引擎节点:在rule-engine模块里增加新的RuleNode实现类,编译后注册到规则引擎的组件列表。
  • 前端部件:在ui-ngx的modules/home/components/widget目录下增加自定义部件类型,前端单独跑开发服务器调试。
  • REST API扩展:在application模块的controller包下新增Controller,注意鉴权注解和权限配置。

这样分工维护起来清晰很多,也不容易把别人写的代码冲掉。每次只改一个模块,然后重新mvn install对应模块,启动速度反而比全量快得多。

5.3 关于构建产物和版本管理的经验

最后说点构建产物和版本管理的经验。源码编译出来的服务目录结构是这样:application/target下有一个以thingsboard命名的目录,里面包含可执行启动脚本。如果是在macOS上直接跑主类,其实可以不依赖这个目录,但一旦需要部署到服务器,还是要用完整产物。

我个人的习惯是保持根目录干净,修改配置文件只用thingsboard.yml,不把本地数据库密码写到公共配置里。可以创建一个thingsboard-dev.yml,通过启动参数覆盖:

java -jar thingsboard.jar --spring.config.additional-location=file:./thingsboard-dev.yml

这样既保留了官方默认配置,又能让本地调试参数不污染仓库里的公共文件。另一个习惯是拉取新版本代码前,先把~/.m2/repository里跟thingsboard相关的旧快照清掉,不然老版本依赖会影响新代码的编译结果。反正编译一个物联网平台本来就是个重活,环境越干净,后面跑起来越省心。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询