Стандартные JSON.parse()
и JSON.stringify()
являются синхронными операциями и могут блокировать цикл событий при работе с большими данными:
// Проблемный код: блокировка потока
app.post('/api/data', (req, res) => {
const largeData = JSON.parse(fs.readFileSync('large.json')); // Двойная блокировка!
const result = processData(largeData);
res.json(result); // Еще один JSON.stringify
});
Использование потоков для постепенной обработки:
const { pipeline } = require('stream');
const { parse } = require('JSONStream');
app.post('/stream-parse', (req, res) => {
const transform = new Transform({
objectMode: true,
transform(chunk, enc, cb) {
// Обработка частей данных
cb(null, processChunk(chunk));
}
});
pipeline(
req, // Входящий поток
parse('*'), // Потоковый парсер
transform,
res, // Исходящий поток
(err) => err && console.error(err)
);
});
В новых версиях Node.js (v21+):
import { parse, stringify } from 'node:json';
app.post('/async-json', async (req, res) => {
const chunks = [];
for await (const chunk of req) {
chunks.push(chunk);
}
const data = await parse(Buffer.concat(chunks));
res.json(await processAsync(data));
});
Для действительно больших JSON:
const { Worker } = require('worker_threads');
function parseInWorker(jsonStr) {
return new Promise((resolve, reject) => {
const worker = new Worker(`
const { parentPort } = require('worker_threads');
parentPort.postMessage(JSON.parse(${JSON.stringify(jsonStr)}));
`, { eval: true });
worker.on('message', resolve);
worker.on('error', reject);
});
}
app.post('/worker-parse', async (req, res) => {
const data = await parseInWorker(req.body);
res.json(data);
});
Экстремально быстрый парсер:
const simdjson = require('simdjson');
app.post('/simd', (req, res) => {
const data = simdjson.lazyParse(fs.readFileSync('large.json'));
res.json(data);
});
Предварительная схема для ускорения:
const fastJson = require('fast-json-stringify');
const stringify = fastJson({
title: 'Example',
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' }
}
});
app.get('/fast', (req, res) => {
res.header('Content-Type', 'application/json');
res.send(stringify({ id: '1', name: 'Fast' }));
});
Используйте JSONPath для выборки только нужных данных:
const jsonpath = require('JSONPath');
app.post('/partial', (req, res) => {
const data = jsonpath.query(largeJson, '$..items[?(@.priority > 3)]');
res.json(data);
});
Использование бинарного формата:
const BSON = require('bson');
app.post('/bson', (req, res) => {
const data = BSON.deserialize(req.body);
const output = BSON.serialize(process(data));
res.send(output);
});
Для часто используемых данных:
const NodeCache = require('node-cache');
const jsonCache = new NodeCache({ stdTTL: 3600 });
app.get('/cached', (req, res) => {
const cacheKey = req.originalUrl;
const cached = jsonCache.get(cacheKey);
if (cached) return res.json(cached);
const data = processData();
jsonCache.set(cacheKey, data);
res.json(data);
});
Защита от слишком больших запросов:
app.use(express.json({ limit: '100kb' })); // Ограничение тела запроса
app.use((err, req, res, next) => {
if (err.type === 'entity.too.large') {
res.status(413).send('Payload too large');
}
});
Главные правила: