
一、介紹 Node-RED 是由 IBM 新興技術團隊開發并開源的一款基于流程編程的低代碼開發工具,專為物聯網和工業自動化場景設計 ?!?底層核心:基于 Node.js 構建,本質是一個運行在服務器(或邊緣設備)上的 Web 應用程序 ?!?運行機制:通過瀏覽器中的可視化編輯器,將預制的節點(Node)拖拽到畫布上并連接,形成流(Flow)。數據以 JSON 格式在節點間傳遞,通過配置即可實現業務邏輯,無需大量手寫代碼 ?!?應用場景:廣泛用于工業數據采集(支持 Modbus、OPC UA、MQTT 等協議)、協議轉換、實時數據處理以及與數據庫(如 InfluxDB、TDengine、SQLite)或硬件(如 GPIO)的交互 。 本質上,它是一個在瀏覽器里“畫流程圖”就能編程的工業物聯網中間件。二、安裝Node-RED 安裝教程(Linux / Docker 版)2.1、Linux 原生安裝1. 安裝 Node.js(≥18.x)node.js卸載、安裝、配置詳解# Ubuntu/Debian curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - sudo apt install -y nodejs # CentOS/RHEL curl -fsSL https://rpm.nodesource.com/setup_18.x | sudo bash - sudo yum install -y nodejs# 驗證安裝node --version npm --version2. 安裝 Node-REDnode-red:環境搭建及使用sudo npm install -g --unsafe-perm node-red3. 啟動運行# 后臺服務化(生產用)sudo systemctl enable nodered.service # 設置開機自啟 sudo systemctl start nodered.service # 啟動服務4. 訪問地址瀏覽器打開:http://本機IP:18805、pm2管理node-red1、pm2給應用重命名 pm2 restart node-red --name juxinnode 2、設置應用超180M就重啟 pm2 start juxinnode --max-memory-restart 180M 3、docker未設置掛載的node-red的配置文件在/data/node-red下面 pm2 restart 0 --cron "0 1 * * *" export EDITOR=vi #編輯定時任務2.2、Docker 安裝node-red參考資料docker run -u root:node-red -v /home/node3:/data --privileged=true -p 1883:1880 --name node_red3 -d mynodered:1.2 --restart=always 統一時間: docker cp /usr/share/zoneinfo/Asia/Shanghai node_red4:/etc/localtime 首先進入容器內 docker exec -it ID /bin/bash 安裝插件 npm install node-red-contrib-modbus2.2.1、使用 Docker Compose創建 docker-compose.yml:version: '3' services: nodered: image: nodered/node-red container_name: nodered ports: - "1880:1880" volumes: - ./node-red-data:/data restart: always啟動:docker-compose up -d訪問地址瀏覽器打開:http://Docker宿主機IP:18802.2.2、常用命令速查操作 Linux 原生 Docker 啟動 node-red 或 systemctl start nodered docker start nodered 停止 systemctl stop nodered docker stop nodered 重啟 systemctl restart nodered docker restart nodered 日志 node-red-log 或 journalctl -u nodered docker logs nodered 開機自啟 systemctl enable nodered restart: always(compose)2.2.3、驗證安裝任意瀏覽器訪問 http://服務器IP:1880,看到可視化編輯界面即成功。三、節點介紹3.1、influxdb節點"1、安裝influxdb插件node-red-contrib-influxdb 2、配置Influxdb參數設置數據庫以及表名稱 3、編寫函數 var object=msg.payload.data; msg.payload = [{ name:""tjn"", datas:object[0], timestamp: new Date()}]; return msg 記住是json的key value的格式就對了! 然后點擊部署,查看數據庫,你就可以發現插入的數據了! 總結:表名稱要寫,不然會出錯!"3.2、sqlserver節點1、使用node-red-contrib-mssql-plus 這個節點 2、雙擊節點,在Query里面輸入SELECT Tray_ID as code,CDT FROM HIS_TCOMMANDS WHERE CDT = DATEADD(MINUTE, -10, GETDATE()) AND PLCType = 2 AND TaskMode = 2 ORDER BY CDT DESC3.3、安裝modbus節點npm install -g node-red-contrib-modbus --unsafe-perm --build-from-source 若失敗報錯 make: g++:命令未找到 make: *** [Release/obj.target/bindings/src/serialport.o] 解決方案 make是gcc的編譯器,VPS買來必定要安裝 安裝: yum -y install gcc automake autoconf libtool make 安裝g++: yum install gcc gcc-c++3.4、fs模塊異步讀寫文件// 使用示例(異步版本) (async function () { try { const filePath = '/home/juxin/test.txt'; // 讀文件 - 使用 await const myContent = await readAndTrimAsync(filePath); node.warn("讀取文件內容 " + myContent); // 寫文件 - 使用 await const result1 = await writeFileCoverAsync(filePath, '周受資,清雅絕倫君子面', { log: node.warn, trim: true }); } catch (error) { // 處理錯誤 msg.payload = { error: error.message, success: false }; } // 確保返回msg return msg; })(); // ============================ // 異步文件讀取函數 // ============================ async function readAndTrimAsync(filePath) { const fs = global.get('fs'); const fsPromises = fs.promises; // Node.js 10+ 支持 // 基礎驗證 if (!fs) throw new Error('fs模塊不可用'); if (!fsPromises) throw new Error('fs.promises不可用,請升級Node.js到10.0+'); if (!filePath) throw new Error('文件路徑不能為空'); try { // 檢查文件是否存在 - 異步 try { await fsPromises.access(filePath); } catch (error) { throw new Error(`文件不存在或無訪問權限: ${filePath}`); } // 讀取文件 - 異步 let content = await fsPromises.readFile(filePath, 'utf8'); // 空值檢查 if (content === null || content === undefined || content === '') { throw new Error(`文件內容為空: ${filePath}`); } // 去除首尾空格 content = content.trim(); return content; } catch (error) { // 使用node.error記錄錯誤 if (typeof node !== 'undefined' node.error) { node.error(`讀取文件失敗 [${filePath}]: ${error.message}`); } else { console.error(`讀取文件失敗 [${filePath}]:`, error); } throw error; // 重新拋出,讓調用者處理 } } // ============================ // 異步文件覆蓋寫入函數 // ============================ async function writeFileCoverAsync(filePath, content, options = {}) { const fs = global.get('fs'); const fsPromises = fs.promises; const path = global.get('path'); // 基礎驗證 if (!fs) throw new Error('fs模塊不可用'); if (!fsPromises) throw new Error('fs.promises不可用,請升級Node.js到10.0+'); if (!filePath) throw new Error('文件路徑不能為空'); if (content === undefined || content === null) { throw new Error('寫入內容不能為空'); } // 默認選項 const defaults = { encoding: 'utf8', createDir: true, // 自動創建目錄 log: null, // 日志函數,如 node.warn backup: false, // 是否備份原文件 backupSuffix: '.bak', // 備份文件后綴 trim: false // 是否自動去除內容首尾空格 }; const opts = { ...defaults, ...options }; try { // 1. 處理內容(可選trim) let processedContent = content; if (opts.trim typeof processedContent === 'string') { processedContent = processedContent.trim(); } // 如果是對象,轉為JSON字符串 if (typeof processedContent === 'object') { processedContent = JSON.stringify(processedContent, null, 2); } // 2. 自動創建目錄(如果需要)- 異步 if (opts.createDir path) { const dir = path.dirname(filePath); try { await fsPromises.access(dir); } catch { // 目錄不存在,創建它 await fsPromises.mkdir(dir, { recursive: true }); if (opts.log) opts.log(`?? 已創建目錄: ${dir}`); } } // 3. 備份原文件(如果需要)- 異步 if (opts.backup) { try { await fsPromises.access(filePath); const backupPath = filePath + opts.backupSuffix; await fsPromises.copyFile(filePath, backupPath); if (opts.log) opts.log(`?? 已備份原文件至: ${backupPath}`); } catch { // 原文件不存在,無需備份 } } // 4. 覆蓋寫入文件 - 異步 await fsPromises.writeFile(filePath, processedContent, opts.encoding); // 5. 驗證寫入成功 - 異步 const stats = await fsPromises.stat(filePath); const fileSize = stats.size; // 6. 記錄日志 if (opts.log) { opts.log(`? 文件寫入成功: ${filePath}`); opts.log(` 文件大小: ${fileSize} 字節`); if (typeof content === 'string') { opts.log(` 內容長度: ${processedContent.length} 字符`); } } return { success: true, filePath: filePath, size: fileSize, timestamp: new Date().toISOString() }; } catch (error) { const errMsg = `寫入文件失敗 [${filePath}]: ${error.message}`; if (opts.log) { opts.log(`? ${errMsg}`); } throw new Error(errMsg); } } // ============================ // 批量操作輔助函數 // ============================ // 批量讀取多個文件 async function readMultipleFilesAsync(filePaths) { const readPromises = filePaths.map(filePath = readAndTrimAsync(filePath).catch(error = { // 返回錯誤信息而不是拋出,確保其他文件可以繼續讀取 return { filePath, error: error.message, success: false }; }) ); return await Promise.all(readPromises); } // 批量寫入多個文件 async function writeMultipleFilesAsync(fileContentMap, options = {}) { const writePromises = Object.entries(fileContentMap).map(([filePath, content]) = writeFileCoverAsync(filePath, content, options).catch(error = { // 返回錯誤信息 return { filePath, error: error.message, success: false }; }) ); return await Promise.all(writePromises); }3.5、實現遠程服務器性能監測https://blog.csdn.net/weixin_44462773/article/details/149119994?spm=1011.2415.3001.53313.6、監測超融合Proxmox VE集群狀態數據https://blog.csdn.net/weixin_44462773/article/details/148919884?spm=1011.2415.3001.53313.7、sqlite節點node-red-node-sqlite對比維度核心優勢核心劣勢一句話關鍵結論部署運維成本零后臺服務、單文件存儲,無需安裝配置賬號端口,Node-RED 里填路徑就能用,備份遷移直接復制文件無遠程運維能力,只能本地操作,無自帶主從備份、監控告警零運維門檻,僅適合單機本地部署并發寫入能力單線程寫入無額外開銷,單機低并發寫入響應極快文件級鎖,同一時間僅支持 1 個寫操作,多并發寫入直接排隊阻塞,高頻寫入必卡頓寫并發是致命短板,超過 1 路高頻寫入必出問題數據規模上限單庫理論最大支持 140TB,容量上限足夠單表百萬級數據后,無索引查詢性能暴跌,無原生分庫分表能力,海量數據管理能力弱小數據量順滑,大數據量拉胯,僅適合十萬級以內單表數據讀寫性能純本地文件讀寫,無網絡開銷,低頻次簡單讀寫延遲極低無專業緩存機制,復雜聯表、分組統計 CPU 占用飆升,批量寫入不加事務會極慢簡單讀寫快,復雜查詢 / 批量寫入性能遠低于專業數據庫功能完整性支持標準 SQL、事務、索引,完全覆蓋日常增刪改查、建表需求無用戶權限、存儲過程、觸發器能力弱,無主從、集群、分表等企業級能力基礎功能夠用,高級功能幾乎為零數據安全與可靠性支持事務原子性,單文件備份簡單,正常斷電數據無丟失寫入中途強制斷電、閃存損壞有小概率文件損壞,無自動恢復機制正常使用安全,極端故障場景有數據丟失風險跨設備共享能力無僅支持本地進程讀寫,無法多設備遠程連接共享數據庫完全不支持分布式場景,多設備數據匯總只能單機單獨存后人工合并硬件適配性超輕量,CPU / 內存占用極低,樹莓派、工控機、網關等低配硬件完美適配無嵌入式 / 低硬件配置場景適配性拉滿SQLite 是 **「單機輕量場景的最優解,高并發分布式場景的絕對禁區」**,它的所有優勢都來自「極簡無服務」,所有劣勢也都來自「極簡無服務」,沒有任何中間地帶。?必用場景:單臺本地網關 / 工控機、低頻率數據存儲(分鐘級采集、本地日志、配置表)、單機自動化流程、無運維需求的小型項目 ?絕對別用:多設備高頻并發上報、需要多機共享數據庫、百萬級以上長期數據存儲、7×24 小時不間斷時序采集避坑核心只要你的場景有「超過 1 個節點同時寫入」,直接放棄 SQLite,必出阻塞、丟數據問題;不要試圖用 SQLite 做海量歷史數據存儲,單表超過 100 萬行后查詢性能會斷崖式下跌;樹莓派 / SD 卡高頻寫入場景,必須加事務、定時歸檔,否則會加速閃存磨損,大概率出現文件損壞。 4 sqlite全方位性能碾壓記事本[ { "id": "81c6b6105cd12496", "type": "inject", "z": "100b4b115fb10d1e", "name": "創建", "props": [ { "p": "opr", "v": "1", "vt": "num" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "x": 370, "y": 160, "wires": [ [ "0e3b37521a2fd70f" ] ] }, { "id": "0e3b37521a2fd70f", "type": "function", "z": "100b4b115fb10d1e", "name": "插入", "func": "if(msg.opr == 1){\n //創建語句\n msg.topic = `CREATE TABLE IF NOT EXISTS users (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n email TEXT NOT NULL)`;\n}\n\nelse if (msg.opr == 2) {\n //插入數據(INSERT)\n msg.topic = `INSERT INTO users (name, email) VALUES ($name, $email);`\n msg.payload = [\"alice\", \"alice@163.com\"];\n}\n\nelse if (msg.opr == 3) {\n//更新數據(UPDATE)\n msg.topic = \"UPDATE users SET email = $email WHERE name = $name\";\n msg.payload = [\"new_email@1example.com\", \"alice\"];\n}\n\nelse if (msg.opr == 4) {\n //刪除數據(DELETE)\n msg.topic = \"DELETE FROM users WHERE id = $id\";\n msg.payload = [1]; // 刪除id為1的記錄\n}\nelse{\n msg.topic = \"SELECT * FROM users;\";\n msg.payload = [];\n}\n\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 590, "y": 260, "wires": [ [ "fb1339cb98ec300e" ] ] }, { "id": "fb1339cb98ec300e", "type": "sqlite", "z": "100b4b115fb10d1e", "mydb": "e90adb5af2580d03", "sqlquery": "msg.topic", "sql": "", "name": "", "x": 770, "y": 260, "wires": [ [ "41ecf6106bcddffb" ] ] }, { "id": "41ecf6106bcddffb", "type": "debug", "z": "100b4b115fb10d1e", "name": "debug 61", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 960, "y": 260, "wires": [] }, { "id": "6cc14b8c94bd04ae", "type": "inject", "z": "100b4b115fb10d1e", "name": "插入",