在RISC-V架构芒果派上部署Node.js与EMQX物联网开发环境
1. 项目概述与核心需求解析
最近在折腾一块芒果派 MangoPi MQ Quad 开发板,这是一款基于全志 D1s 处理器的 RISC-V 架构板子,性能不错,功耗也低,很适合用来做一些物联网边缘端的应用。我的目标是在这块板子上搭建一个能够运行 Node.js 后端服务,并集成 EMQX MQTT 消息中间件的环境,为后续的智能家居数据网关或者设备管理平台打基础。对于嵌入式开发或者物联网项目来说,直接在目标硬件上配置好稳定、可维护的开发环境是第一步,也是最关键的一步,能避免后续很多“水土不服”的问题。
这个需求很明确:我们需要一个灵活的 Node.js 环境来编写和运行业务逻辑,同时需要一个轻量且高性能的 MQTT Broker(EMQX)来处理设备与服务器、设备与设备之间的消息通信。直接在 ARM 或 RISC-V 架构的 Linux 系统上安装这些环境,和我们在 x86 的云服务器上操作,思路类似但细节上会遇到不少差异,尤其是包管理、依赖库和编译工具链方面。接下来,我就把在芒果派 MQ Quad 上从零开始部署 Node.js 和 EMQX 的完整过程、遇到的坑以及解决方案详细记录下来,如果你也在类似的嵌入式 Linux 平台上做部署,这份经验应该能帮你省下不少时间。
2. 环境准备与系统基础配置
在开始安装具体软件之前,我们需要先确保芒果派 MQ Quad 的系统处于一个良好的基础状态。我使用的是官方推荐的 Tina Linux 系统(基于 OpenWrt 改造)或 Debian 镜像,这里以 Debian 为例,因为它有更完善的 apt 包管理工具,后续安装会更方便。
2.1 系统更新与基础依赖安装
首先,通过 SSH 连接到你的芒果派。建议在操作前更新一下软件源列表并升级已有的软件包,这能确保我们安装的依赖是最新的,减少兼容性问题。
sudo apt update sudo apt upgrade -y更新完成后,安装一些编译和基础运行所需的工具链。在嵌入式设备上从源码编译或安装二进制包时,这些工具常常是必需的。
sudo apt install -y curl wget git build-essential libssl-dev ca-certificates注意:
build-essential包含了 gcc、g++、make 等基础编译工具。libssl-dev是 Node.js 编译和运行某些加密模块所必需的开发库。在资源受限的设备上,如果镜像空间紧张,可以酌情不安装build-essential,前提是你确定要安装的软件都有对应架构的预编译二进制包。
2.2 硬件与网络确认
芒果派 MQ Quad 的 CPU 是全志 D1s,这是一颗单核的 RISC-V 64 处理器。我们可以通过以下命令确认架构:
uname -m输出应该是riscv64。这一点非常重要,因为后续我们寻找或编译软件时,都必须针对riscv64或riscv架构。Node.js 官方提供了 ARM 和 x86 的预编译包,但对于 RISC-V,情况就复杂一些。同样,EMQX 的官方 apt 源也主要面向 x86_64 和 ARM64,我们需要确认是否有 RISC-V 的版本,或者寻找替代的安装方式。
3. Node.js 环境部署:使用 NVM 进行灵活管理
在开发板上直接安装特定版本的 Node.js 二进制包可能比较困难,因为官方不一定提供对应架构的版本。因此,我选择使用 NVM (Node Version Manager) 来管理 Node.js。NVM 的优势在于它可以通过源码编译的方式安装 Node.js,这样就能适配任何支持的架构,包括我们的 RISC-V。
3.1 NVM 的工作原理与选型考量
NVM 本质上是一个 shell 脚本,它通过修改用户的环境变量(主要是PATH)来切换当前 shell 会话中使用的 Node.js 版本。它会将不同版本的 Node.js 安装在用户家目录下的独立文件夹中(例如~/.nvm/versions/node/),互不干扰。为什么选择 NVM 而不是直接apt install nodejs?
- 版本控制自由:Debian 系统源里的 Node.js 版本往往比较旧。而物联网项目可能依赖较新的 Node.js 特性,或者需要与云端服务保持版本一致,NVM 可以安装任意官方发布的版本。
- 多版本共存:同一个板子上可能运行多个不同的 Node.js 应用,它们要求的 Node.js 版本可能不同。NVM 可以让你为每个项目(甚至每个终端会话)快速切换版本。
- 规避系统污染:将 Node.js 安装在用户目录下,不需要
sudo权限,避免了因误操作影响系统稳定性,卸载也更为干净。
3.2 使用国内源安装 NVM
NVM 的官方安装脚本托管在 GitHub,从国内访问可能速度慢或不稳定。幸运的是,有国内开发者维护了镜像和安装脚本。我们采用一个修改后的脚本来加速安装过程。
首先,下载国内社区维护的安装脚本:
curl -fsSL https://gitee.com/RubyKids/nvm-cn/raw/main/install.sh -o install.sh下载后,不要直接运行。我们需要先检查并修改脚本,确保它从国内的 Gitee 镜像仓库克隆 NVM 源码,而不是 GitHub。用vim或nano打开install.sh文件:
nano install.sh找到脚本中克隆仓库的部分(通常在靠前的位置,例如原脚本的34-40行附近)。我们需要将git clone的源地址从 GitHub 改为 Gitee 的镜像地址。修改后的关键片段应如下所示:
export NVM_DIR="$HOME/.nvm" && ( echo "=> Git clone nvm" git clone https://gitee.com/mirrors/nvm.git "$NVM_DIR" cd "$NVM_DIR" git checkout `git describe --abbrev=0 --tags --match "v[0-9]*" $(git rev-list --tags --max-count=1)` ) && . "$NVM_DIR/nvm.sh"这里将仓库地址改为了https://gitee.com/mirrors/nvm.git,这是 NVM 的一个国内镜像,下载速度会快很多。
修改保存后,执行安装:
bash install.sh安装脚本会自动克隆 NVM 仓库到~/.nvm目录,并将初始化命令添加到你的 shell 配置文件(如~/.bashrc或~/.zshrc)中。
安装最后,脚本可能会提示你输入密码,以尝试将nvm-update脚本安装到/usr/local/bin/。在嵌入式设备上,这一步通常可以跳过。直接按Ctrl + C终止即可,这不会影响 NVM 的核心功能。我们后续更新 NVM 本身完全可以在用户目录下进行。
安装完成后,需要重新加载 shell 配置,或者新开一个终端窗口,以使nvm命令生效:
source ~/.bashrc # 或者如果你用的是 zsh: source ~/.zshrc现在,可以验证 NVM 是否安装成功:
nvm --version如果输出版本号(如0.39.7),说明安装成功。
3.3 在 RISC-V 架构上安装 Node.js
这是最关键也最容易出问题的一步。直接运行nvm install --lts或nvm install node,NVM 会尝试下载对应架构的预编译二进制包。但 Node.js 官方不提供riscv64的预编译包,因此下载会失败。
NVM 的 fallback 机制是:如果找不到预编译二进制包,它会尝试从源码编译。编译 Node.js 对设备的内存和 CPU 有一定要求,好在芒果派 MQ Quad 的 512MB 内存勉强够用,但编译过程会非常漫长(可能需要数小时)。
操作步骤与核心命令:
指定从源码编译:我们可以通过
-s参数明确告诉 NVM 从源码安装。nvm install -s 18.19.0这里我选择了 18.19.0 这个 LTS 版本,相对稳定且生态兼容性好。你可以替换成任何你需要的版本号。
漫长的编译过程:执行上述命令后,NVM 会开始下载 Node.js 源码包,并运行
./configure、make、make install。屏幕上会滚动大量的编译信息。在这个过程中,请保持 SSH 连接稳定,避免中断。如果编译因网络或内存不足失败,可以尝试交换分区(swap)来缓解。启用交换文件(可选但强烈建议):编译 Node.js 可能耗尽内存。我们可以临时创建一个交换文件来充当虚拟内存。
# 创建一个 1GB 的交换文件 sudo fallocate -l 1G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile编译完成后,可以关闭并删除交换文件以释放存储空间:
sudo swapoff /swapfile sudo rm /swapfile验证安装:编译安装完成后,使用以下命令查看已安装的版本并设置为默认。
nvm list nvm alias default 18.19.0 node --version npm --versionnvm list会列出所有安装的版本,当前使用的版本前会有一个箭头->。nvm alias default可以设置默认版本,这样新开的终端也会自动使用这个版本。
实操心得与避坑指南:
- 耐心是关键:在单核 RISC-V 上编译 Node.js 可能长达 3-5 小时。建议在晚上或不需要使用板子的时候进行。
- 确保依赖完整:如果编译失败,仔细查看错误日志。常见的失败原因是缺少某些开发库。你可能需要额外安装
python3、pkg-config等。根据错误提示使用apt安装对应的-dev包。- 版本选择:不建议安装太新的奇数版本(如 19.x, 21.x),优先选择 LTS(长期支持)版本,如 18.x 或 20.x,它们在嵌入式环境下的稳定性和社区支持更好。
- NPM 源优化:安装好 Node.js 后,建议立即将 NPM 的注册表源换成国内镜像,加速后续安装包的速度。
npm config set registry https://registry.npmmirror.com/
4. EMQX MQTT Broker 部署与配置
Node.js 环境就绪后,我们需要部署 MQTT 消息服务器。EMQX 是一个开源的高性能 MQTT 5.0 Broker,用 Erlang/OTP 语言编写,非常适合物联网场景,支持海量并发连接。幸运的是,EMQX 官方为多种架构提供了预编译的安装包,其中就包括arm64和riscv64。
4.1 通过官方 Apt 源安装 EMQX
这是最推荐的方式,便于后续管理和升级。
导入 EMQX Apt 仓库的 GPG 密钥并添加源:
# 安装必要的工具以处理 apt 仓库 sudo apt install -y gnupg curl # 添加 GPG 密钥 curl -s https://packages.emqx.io/gpg.pub | sudo apt-key add - # 添加 EMQX Apt 源 echo "deb [arch=riscv64] https://packages.emqx.io/emqx-ce/deb/debian/ ./" | sudo tee /etc/apt/sources.list.d/emqx.list注意
[arch=riscv64]这个部分,它明确指定了这个源用于riscv64架构的包。这是成功的关键。更新本地包索引并安装 EMQX:
sudo apt update sudo apt install emqxapt update会从新添加的 EMQX 源拉取软件包信息。如果一切顺利,apt install emqx就会下载并安装针对 RISC-V 64 架构编译好的 EMQX 软件包及其所有依赖。
4.2 管理 EMQX 服务
EMQX 安装后,会作为一个系统服务(systemd service)运行。
启动与启用开机自启:
sudo systemctl start emqx # 立即启动服务 sudo systemctl enable emqx # 设置开机自动启动检查服务状态:
sudo systemctl status emqx如果状态显示为
active (running),则说明 EMQX 已成功启动。使用 EMQX CLI 工具验证: EMQX 提供了一个命令行工具来检查 Broker 状态。
emqx ctl broker这个命令会输出 EMQX 的版本、运行时间、集群状态等概要信息。看到类似
Node ‘emqx@127.0.0.1’ started的输出,就证明 Broker 核心运行正常。
4.3 访问 Web 管理控制台并进行基础安全配置
EMQX 默认开启了基于 Web 的管理控制台,这对于监控和配置来说非常方便。
确定访问地址:首先获取芒果派的 IP 地址。
ip addr show找到
eth0(有线网卡)或wlan0(无线网卡)对应的inet地址。登录控制台:在同一局域网内的电脑浏览器中,输入
http://<你的芒果派IP>:18083。例如http://192.168.50.60:18083。- 默认用户名:
admin - 默认密码:
public
重要:首次登录后,务必立即修改默认密码!在控制台的
Management->Users页面可以修改 admin 用户的密码。- 默认用户名:
基础配置检查:
- 监听端口:在
Management->Listeners页面,确认默认的 MQTT TCP 监听器(端口 1883)和 WebSocket 监听器(端口 8083)是否处于运行状态。这是客户端连接所使用的端口。 - 系统监控:在
Monitoring页面,可以查看连接数、消息吞吐量、系统资源(CPU、内存)使用情况。对于内存只有 512MB 的 MQ Quad,需要密切关注内存使用。
- 监听端口:在
注意事项:
- 防火墙:如果无法从外部访问 18083(管理台)或 1883(MQTT),请检查芒果派系统的防火墙设置(如
ufw)是否放行了这些端口。- 性能考量:EMQX 默认配置是为服务器设计的。在资源受限的嵌入式设备上,可能需要调整 Erlang VM 参数以限制内存使用。配置文件位于
/etc/emqx/emqx.conf。一个简单的优化是减少最大连接数和会话数,例如修改node.max_connections和zone.external.max_connections为一个较小的值(如 1000)。- 数据持久化:默认配置下,消息和会话数据存储在内存中。板子断电会丢失。如果需要对消息进行持久化,需要配置 EMQX 使用外部数据库(如 PostgreSQL)或内置的 Mnesia 数据库持久化,但这会显著增加存储 I/O 和内存开销,需根据项目需求权衡。
5. 环境整合验证与示例应用
部署好 Node.js 和 EMQX 后,我们需要验证它们是否能协同工作。一个经典的测试是:编写一个简单的 Node.js 脚本,使用 MQTT 客户端库连接到本地的 EMQX Broker,进行消息的发布和订阅。
5.1 创建测试项目并安装 MQTT 客户端库
在家目录下创建一个测试目录,并初始化一个 Node.js 项目。
mkdir ~/mqtt-test && cd ~/mqtt-test npm init -y安装一个流行的 MQTT 客户端库,例如mqtt。
npm install mqtt5.2 编写测试脚本
创建一个名为test.js的文件,内容如下:
const mqtt = require('mqtt'); // 连接到本地 EMQX Broker,默认端口 1883 const client = mqtt.connect('mqtt://localhost:1883'); client.on('connect', function () { console.log('✅ 已连接到 MQTT Broker'); // 订阅一个测试主题 client.subscribe('mangopi/test', function (err) { if (!err) { console.log('📨 已订阅主题: mangopi/test'); // 订阅成功后,向该主题发布一条消息 client.publish('mangopi/test', 'Hello from MangoPi MQ Quad!'); } }); }); // 接收消息的回调 client.on('message', function (topic, message) { // message 是 Buffer,需要转成字符串 console.log(`📩 收到消息 [${topic}]: ${message.toString()}`); // 收到消息后,断开连接(仅用于测试) client.end(); }); client.on('error', function (error) { console.error('❌ 连接错误:', error); });这个脚本做了三件事:
- 连接到本机 EMQX。
- 连接成功后,订阅主题
mangopi/test。 - 订阅成功后,立即向同一个主题发布一条消息。
- 当收到自己发布的消息时,打印出来并断开连接。
5.3 运行测试并观察结果
在终端运行脚本:
node test.js预期输出:
✅ 已连接到 MQTT Broker 📨 已订阅主题: mangopi/test 📩 收到消息 [mangopi/test]: Hello from MangoPi MQ Quad!如果看到以上输出,恭喜你!这证明:
- Node.js 环境工作正常,可以执行 JavaScript 代码并加载第三方模块。
- EMQX Broker 工作正常,成功接受了 TCP 连接、处理了订阅和发布请求。
- 整个消息通路(发布 -> Broker -> 订阅)是畅通的。
你还可以打开 EMQX 的 Web 控制台,在Monitoring->Clients和Monitoring->Topics页面,实时看到这个测试客户端的连接和消息流转情况。
6. 常见问题排查与性能优化实录
在实际部署和运行中,你可能会遇到以下问题。这里记录了我遇到的情况和解决方法。
6.1 NVM 及 Node.js 相关问题
问题1:nvm install -s编译失败,提示g++: internal compiler error: Killed (program cc1plus)
- 原因:这通常是编译过程中内存耗尽,系统杀掉了编译进程。RISC-V 架构编译 Node.js 非常消耗内存。
- 解决:
- 增加交换空间:如前文所述,创建并启用一个足够大的交换文件(如 2GB)。
- 并行编译限制:通过环境变量限制
make使用的 job 数量,减少瞬时内存压力。在运行nvm install前执行:export MAKE_JOBS=1 nvm install -s 18.19.0 - 使用更轻量的版本:尝试安装稍旧一点的 LTS 版本(如 Node.js 16.x),其源码可能对内存需求略低。
问题2:使用npm install安装某些原生模块(如sqlite3,bcrypt)失败
- 原因:这些模块包含需要针对本机架构编译的 C++ 代码。编译它们同样需要开发工具和库,且可能依赖特定版本的 Node.js 头文件。
- 解决:
- 确保已安装
build-essential,python3,make,g++。 - 尝试使用
npm config set跳过可选依赖或指定 Python 路径。 - 最根本的,优先寻找纯 JavaScript 实现的替代包(例如用
better-sqlite3替代sqlite3,用bcryptjs替代bcrypt),它们无需编译,在异构架构上兼容性极佳。
- 确保已安装
6.2 EMQX 相关问题
问题3:无法通过浏览器访问http://<ip>:18083
- 排查步骤:
- 确认服务运行:
sudo systemctl status emqx查看状态。 - 确认监听端口:
sudo netstat -tlnp | grep 18083。如果看不到监听,可能是 EMQX 启动失败,查看日志sudo journalctl -u emqx -f。 - 检查防火墙:Debian 可能默认安装了
ufw。运行sudo ufw status。如果状态是active,需要放行端口:sudo ufw allow 18083/tcp和sudo ufw allow 1883/tcp。 - 检查网络:确保你的电脑和芒果派在同一个局域网子网内。
- 确认服务运行:
问题4:EMQX 运行一段时间后,板子内存占用过高,响应变慢
- 分析与优化:
- 监控:通过 Web 控制台或
emqx ctl broker命令查看当前连接数和会话数。嵌入式场景下,连接数应控制在数百以内。 - 调整配置:编辑
/etc/emqx/emqx.conf:- 限制最大连接数:
node.max_connections = 1000(根据实际情况调小) - 减少会话过期时间:
session.max_lifetime = 2h(默认是永不过期) - 关闭非必需功能:如将
log.level从info改为warning减少日志量;评估是否需要开启规则引擎 (rule_engine) 或数据桥接 (bridges),如果不用可以关闭相关配置。
- 限制最大连接数:
- 重启服务:修改配置后,运行
sudo systemctl restart emqx生效。
- 监控:通过 Web 控制台或
问题5:Node.js 应用作为 MQTT 客户端频繁断线重连
- 可能原因:
- 网络不稳定。
- 客户端代码没有正确处理
keepalive和重连逻辑。 - EMQX 侧配置的
keepalive时间太短。
- 解决:
- 在客户端代码中实现稳健的重连机制,监听
close和error事件。 - 适当增加客户端的
keepalive间隔(例如 60 秒)和 EMQX 的zone.external.keepalive配置。 - 检查是否有其他进程占用了大量 CPU,导致客户端心跳响应不及时。
- 在客户端代码中实现稳健的重连机制,监听
6.3 系统层面优化建议
对于芒果派 MQ Quad 这类资源有限的设备,一些系统级的优化能提升整体稳定性:
- 禁用不必要的系统服务:使用
sudo systemctl list-unit-files --type=service查看,禁用如bluetooth,avahi-daemon等可能用不到的服务。 - 使用轻量级进程监控:考虑用
htop替代top,或者使用glances进行更全面的监控。 - 日志管理:定期清理
/var/log目录下的旧日志,防止占满存储。可以配置logrotate或使用journalctl的清理命令。
整个部署过程最耗时的部分就是在 RISC-V 上编译 Node.js,这需要极大的耐心。一旦基础环境搭建完成,后续的应用开发和部署就会顺畅很多。这套组合(Node.js + EMQX)为在芒果派这类边缘设备上构建实时、双向通信的物联网应用提供了一个非常坚实且灵活的基础。
