You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 23:45:34