Если ты используешь
compression() из Express для всех API, ты ломаешь стриминг. Gzip и Brotli ждут полный буфер — res.write() по чанкам не сработает, сжатие начнётся только после res.end(). Это убивает SSE, пагинацию с курсорами и загрузку с прогрессом в production. Частая ошибка: разработчики ставят middleware сжатия глобально, не осознавая, что TTFB растет до момента полной отдачи ответа.Компрессия против буферизации
app.use(compression()); // ждёт весь ответ
app.get('/stream', (req, res) => {
for (let i = 0; i < 1000; i++) res.write(chunk ${i}\n); // не сжимается
res.end(); // только тут сжатие
});
Gzip и Brotli требуют всего ответа для построения словаря. Для потоков данных — например, курсорной пагинации — это неприемлемо. Клиент не получит ни единого байта, пока сервер не завершит поток.
Zstd как решение для стриминга
Zstandard (RFC 8478) спроектирован для потокового сжатия: не накапливает буфер, сжимает на лету. Уровень 1 даёт ~10-20 ГБ/с, что достаточно для большинства серверов. Пример настройки через
node-zstd:import { Transform } from 'stream';
import { compress } from 'node-zstd';
const zstdStream = new Transform({
transform(chunk, _, callback) {
compress(chunk, 3)
.then(c => callback(null, c))
.catch(callback);
}
});
app.get('/api/items', (req, res) => {
res.setHeader('Content-Encoding', 'zstd');
getChunkedDataCursor().pipe(zstdStream).pipe(res);
});Ключевой trade-off: не ставь уровень 22 — он жрёт память как Gzip -9. Для пагинации достаточно level 1-3 с window size 15 (32KB). Для статики можно 22 (4MB), но смотри по RPS.
Бенчмарки и практические советы
* Предкомпилированные словари для повторяющихся структур (DTO) дают до 40% ускорение на бенчмарках.
* На production:
| Алгоритм | TTFB (мс) | Throughput (MB/s) |
|----------|-----------|-------------------|
| Gzip -9 | 450 | 120 |
| Brotli -11| 890 | 85 |
| Zstd -3 | 78 | 980 |
Zstd даёт почти размер Gzip, но в 10 раз быстрее. Без блокировок на whole response — идеально для стриминга и пагинации.
Ошибка: не все браузеры поддерживают Zstd. Всегда проверяй
Accept-Encoding и делай fallback на Brotli для клиентов. Для backend-to-backend коммуникации Zstd однозначно, как делает Netflix.Вывод:
Gzip и Brotli оставь для статики с малым числом запросов; для стриминга, пагинации и SSE используй Zstd с Transform Stream — это даёт на порядок лучший TTFB и пропускную способность без компромиссов по сжатию.
