变量作用域与上下文
变量作用域与上下文
本节介绍 Node-RED 中的变量作用域和上下文管理。学习完成后,您将能够:
- 理解 Node-RED 的三层作用域(局部/Flow/全局)
- 使用 context 在不同节点间共享数据
- 管理上下文数据的生命周期
- 选择合适的存储位置
Variable Scopes Overview
Section titled “Variable Scopes Overview”Node-RED 提供三种变量作用域:
┌──────────────────────────────────────────────────┐│ Node-RED │├──────────────────────────────────────────────────┤│ Global Context (全局作用域) ││ ├─ 所有 Tab 和节点可访问 ││ ├─ 持久化存储 ││ └─ global.get() / global.set() │├──────────────────────────────────────────────────┤│ ┌────────────────────────────────────────────┐ ││ │ Flow Context (Flow 级别作用域) │ ││ │ ├─ 同 Tab 内的节点可访问 │ ││ │ ├─ 持久化存储 │ ││ │ └─ flow.get() / flow.set() │ ││ └────────────────────────────────────────────┘ │├──────────────────────────────────────────────────┤│ ┌────────────────────────────────────────────┐ ││ │ Node Context (节点级别作用域) │ ││ │ ├─ 仅当前节点可访问 │ ││ │ ├─ 持久化存储 │ ││ │ └─ context.get() / context.set() │ ││ └────────────────────────────────────────────┘ │└──────────────────────────────────────────────────┘Context Access Methods
Section titled “Context Access Methods”1. Node Context (节点作用域)
Section titled “1. Node Context (节点作用域)”// 仅在当前 Function 节点内有效// 适合: 节点内部状态、计数器、临时缓存
// 写入context.set("count", 0);context.set("readings", []);
// 读取var count = context.get("count") || 0;var readings = context.get("readings") || [];
// 更新count++;context.set("count", count);2. Flow Context (Flow 作用域)
Section titled “2. Flow Context (Flow 作用域)”// 同 Tab 下的所有节点共享// 适合: Flow 级别配置、共享状态
// 在 Function A 中写入flow.set("threshold", 30);flow.set("deviceConfig", { sensor01: { type: "temperature", interval: 5000 }, sensor02: { type: "humidity", interval: 10000 }});
// 在 Function B 中读取var threshold = flow.get("threshold") || 25;var config = flow.get("deviceConfig") || {};3. Global Context (全局作用域)
Section titled “3. Global Context (全局作用域)”// 所有 Tab 和节点共享// 适合: 系统配置、全局状态、设备注册表
// 写入全局变量global.set("systemStatus", "running");global.set("startTime", Date.now());global.set("devices", { "SENSOR-01": { lastSeen: Date.now(), status: "online" }, "SENSOR-02": { lastSeen: Date.now(), status: "offline" }});
// 读取全局变量var status = global.get("systemStatus");var devices = global.get("devices") || {};Context Persistence
Section titled “Context Persistence”配置持久化存储
Section titled “配置持久化存储”// settings.js 中配置contextStorage: { default: { module: "localfilesystem" // 文件系统存储 }, memory: { module: "memory" // 内存存储(重启丢失) }}# Docker 环境配置services: nodered: image: nodered/node-red:latest environment: - NODE_RED_CONTEXT_STORAGE_DEFAULT=localfilesystem volumes: - ./nodered/data:/data # 持久化目录存储位置对比
Section titled “存储位置对比”| 存储方式 | 持久性 | 性能 | 使用场景 |
|---|---|---|---|
| Default (内存) | 重启丢失 | 最快 | 临时数据 |
| localfilesystem | 文件持久化 | 中等 | 生产环境 |
| Redis | 外部存储 | 快 | 多实例共享 |
| Memory (明确配置) | 重启丢失 | 快 | 缓存数据 |
Practical Examples
Section titled “Practical Examples”Example 1: 设备在线状态跟踪
Section titled “Example 1: 设备在线状态跟踪”// 跟踪设备最后上线时间var devices = context.global.devices || {};if (!devices) { devices = {}; context.global.devices = devices;}
// 更新设备状态var deviceId = msg.payload.device || msg.topic;devices[deviceId] = { lastSeen: Date.now(), status: "online", value: msg.payload.value || msg.payload};
// 检查离线设备(超过 30 秒未更新)var now = Date.now();var offlineDevices = [];for (var id in devices) { if (now - devices[id].lastSeen > 30000) { devices[id].status = "offline"; offlineDevices.push(id); }}
msg.payload = { online_count: Object.keys(devices).length, offline_count: offlineDevices.length, offline_devices: offlineDevices};
return msg;Example 2: 数据累积计算
Section titled “Example 2: 数据累积计算”// 计算滑动窗口平均值var WINDOW_SIZE = 10;
// 节点作用域 - 存储当前读数var readings = context.get("readings") || [];
readings.push(msg.payload);
// 保持窗口大小if (readings.length > WINDOW_SIZE) { readings.shift();}
context.set("readings", readings);
// 计算统计var sum = readings.reduce((a, b) => a + b, 0);
msg.payload = { current: msg.payload, average: (sum / readings.length).toFixed(2), min: Math.min(...readings), max: Math.max(...readings), count: readings.length};
return msg;Example 3: 共享配置管理
Section titled “Example 3: 共享配置管理”// 使用全局变量管理系统配置// 在初始化节点(On Start)中设置
// On Start 代码:var config = { temperature: { warning: 30, critical: 40, unit: "celsius" }, humidity: { warning: 70, critical: 85, unit: "percent" }, intervals: { data_collection: 5000, dashboard_refresh: 10000 }};
flow.set("config", config);global.set("systemName", "Factory IoT Monitor");global.set("version", "1.2.0");
// 在所有节点中均可访问// 在其他 Function 中:var config = flow.get("config");var threshold = config.temperature.warning;Context Best Practices
Section titled “Context Best Practices”| 作用域 | 推荐用途 | 不推荐用途 |
|---|---|---|
| Node | 计数器、窗口数据 | 配置参数 |
| Flow | 模块配置、共享状态 | 系统级数据 |
| Global | 系统配置、设备注册表 | 临时变量 |
Common Customer Questions
Section titled “Common Customer Questions”Q1: 重启后 context 数据会丢失吗?
Section titled “Q1: 重启后 context 数据会丢失吗?”A: 如果使用默认(内存)存储,重启会丢失。生产环境应配置 localfilesystem 或 Redis 持久化存储。
Q2: context 可以存储多少数据?
Section titled “Q2: context 可以存储多少数据?”A: 没有硬性限制,但建议保持在合理范围(数百 KB 以内)。大量数据建议存储到外部数据库。
Q3: 如何在多个 Node-RED 实例间共享 context?
Section titled “Q3: 如何在多个 Node-RED 实例间共享 context?”A: 使用 Redis 作为 context 存储后端,或多个实例连接同一数据库。
✅ 推荐做法:
- 最小作用域原则:优先使用 node context
- 生产环境配置持久化存储
- 定期清理不再使用的 context 变量
- 使用有意义的变量名
- 初始化时设置默认值
❌ 避免做法:
- 滥用全局变量
- 存储大量数据到 context
- 不设置持久化存储
- 变量名冲突(尤其是在全局作用域)
- 存储敏感信息(如密码)
Summary
Section titled “Summary”- Node-RED 提供三层作用域:Node/Flow/Global
- Node context 适合节点内部状态
- Flow context 适合模块级数据共享
- Global context 适合系统级数据和配置
- 生产环境必须配置 context 持久化