NodeJS服务近10分钟符合条件数据的持久化方案咨询
解决方案
1. 使用Redis(推荐,适配临时过期数据场景)
Redis原生支持键自动过期,刚好匹配“保留近10分钟数据”的需求,性能优于文件存储,适合高并发场景。
实现步骤:
- 安装依赖:
npm install redis - 初始化Redis客户端并修改接口逻辑:
const redis = require('redis'); const { promisify } = require('util'); const parseString = promisify(xml2js.parseString); // 将xml2js回调转为Promise风格 // 初始化Redis客户端(需配置地址/密码自行添加参数) const redisClient = redis.createClient(); redisClient.connect().catch(console.error); app.get("/report", async (req, res) => { try { const xml_string = await fetchXML(drones_url); const json = await parseString(xml_string); const drones = json['report'].capture['0'].drone; const tenMinutes = 600; // 单位:秒 // 1. 抓取新违规数据并存入Redis,设置10分钟过期 for (let i = 0; i < drones.length; ++i) { const drone = drones[i]; if (pointIsInNDZ(drone.positionX, drone.positionY)) { const fetch_response = await fetch(pilot_url + drone.serialNumber[0]); if (fetch_response.ok) { const pilot_json = await fetch_response.json(); const storedData = { ...pilot_json, capturedAt: Date.now() }; // 以无人机序列号为键,自动10分钟后过期 await redisClient.setEx(drone.serialNumber[0], tenMinutes, JSON.stringify(storedData)); } } } // 2. 从Redis拉取所有未过期的违规数据 const allKeys = await redisClient.keys('*'); const violations = []; for (const key of allKeys) { const dataStr = await redisClient.get(key); if (dataStr) violations.push(JSON.parse(dataStr)); } res.json(violations); } catch (err) { console.error(err); res.status(500).json({ error: 'Server error' }); } });
2. 使用SQLite(轻量文件数据库,适合持久化场景)
如果需要长期留存数据,或不想依赖外部服务,SQLite是文件型数据库,无需额外部署,适合中小规模应用。
实现步骤:
- 安装依赖:
npm install sqlite3 - 初始化数据库并修改接口:
const sqlite3 = require('sqlite3').verbose(); const { promisify } = require('util'); const parseString = promisify(xml2js.parseString); // 初始化数据库连接 const db = new sqlite3.Database('./violations.db'); const dbRun = promisify(db.run.bind(db)); const dbAll = promisify(db.all.bind(db)); // 首次运行创建表 db.run(`CREATE TABLE IF NOT EXISTS violations ( serialNumber TEXT PRIMARY KEY, pilotData TEXT, capturedAt INTEGER )`); app.get("/report", async (req, res) => { try { const xml_string = await fetchXML(drones_url); const json = await parseString(xml_string); const drones = json['report'].capture['0'].drone; const tenMinutesAgo = Date.now() - 10 * 60 * 1000; // 1. 插入/更新违规数据(同序列号自动覆盖) for (let i = 0; i < drones.length; ++i) { const drone = drones[i]; if (pointIsInNDZ(drone.positionX, drone.positionY)) { const fetch_response = await fetch(pilot_url + drone.serialNumber[0]); if (fetch_response.ok) { const pilot_json = await fetch_response.json(); await dbRun(` INSERT OR REPLACE INTO violations (serialNumber, pilotData, capturedAt) VALUES (?, ?, ?) `, [drone.serialNumber[0], JSON.stringify(pilot_json), Date.now()]); } } } // 2. 查询近10分钟的违规数据 const rows = await dbAll(` SELECT pilotData FROM violations WHERE capturedAt > ? `, [tenMinutesAgo]); const violations = rows.map(row => JSON.parse(row.pilotData)); // 可选:清理过期数据(避免数据库膨胀) await dbRun(`DELETE FROM violations WHERE capturedAt <= ?`, [tenMinutesAgo]); res.json(violations); } catch (err) { console.error(err); res.status(500).json({ error: 'Server error' }); } });
3. 使用本地JSON文件(极简方案,适合小流量场景)
如果数据量小、并发低,可直接用本地JSON文件存储,缺点是高并发下可能出现写入冲突,需额外处理锁机制。
实现代码:
const fs = require('fs').promises; const path = require('path'); const { promisify } = require('util'); const parseString = promisify(xml2js.parseString); const dataFilePath = path.join(__dirname, 'violations.json'); // 初始化数据文件(不存在则创建空数组) async function initDataFile() { try { await fs.access(dataFilePath); } catch { await fs.writeFile(dataFilePath, JSON.stringify([])); } } initDataFile(); app.get("/report", async (req, res) => { try { const xml_string = await fetchXML(drones_url); const json = await parseString(xml_string); const drones = json['report'].capture['0'].drone; const tenMinutesAgo = Date.now() - 10 * 60 * 1000; // 1. 读取现有数据并过滤过期条目 let existingData = JSON.parse(await fs.readFile(dataFilePath, 'utf8')); existingData = existingData.filter(item => item.capturedAt > tenMinutesAgo); // 2. 处理新违规数据,按序列号去重 for (let i = 0; i < drones.length; ++i) { const drone = drones[i]; if (pointIsInNDZ(drone.positionX, drone.positionY)) { const fetch_response = await fetch(pilot_url + drone.serialNumber[0]); if (fetch_response.ok) { const pilot_json = await fetch_response.json(); const newItem = { ...pilot_json, serialNumber: drone.serialNumber[0], capturedAt: Date.now() }; // 替换已存在的同序列号数据,否则新增 const index = existingData.findIndex(item => item.serialNumber === newItem.serialNumber); if (index !== -1) { existingData[index] = newItem; } else { existingData.push(newItem); } } } } // 3. 写入更新后的数据到文件 await fs.writeFile(dataFilePath, JSON.stringify(existingData, null, 2)); res.json(existingData); } catch (err) { console.error(err); res.status(500).json({ error: 'Server error' }); } });
额外优化提示:
- 原代码中
xml2js.parseString的回调写法改为Promise风格,避免回调地狱,更贴合async/await的异步处理逻辑。 - 所有异步操作需包裹try/catch,防止未捕获异常导致服务器崩溃。
内容的提问来源于stack exchange,提问作者Pham Long
相关产品推荐
相关产品推荐

