Загрузка…
Загрузка…
SSE server — long-lived HTTP response с text/event-stream; client — EventSource с auto-reconnect. Blank line после data: — обязательный terminator.
// server (Node/Express) — a stream that never returns
app.get('/prices', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
'X-Accel-Buffering': 'no', // tell nginx NOT to buffer the stream
});
const since = req.headers['last-event-id']; // resume support
const timer = setInterval(() => {
res.write(`id: ${nextId()}\n`);
res.write(`event: price\n`);
res.write(`data: ${JSON.stringify(getPrice())}\n\n`); // blank line!
}, 1000);
const beat = setInterval(() => res.write(': ping\n\n'), 15000); // comment = heartbeat
req.on('close', () => { clearInterval(timer); clearInterval(beat); });
});// client — the browser handles reconnect + Last-Event-ID for you
const es = new EventSource('/prices'); // add { withCredentials: true } for cookies
es.addEventListener('price', (e) => render(JSON.parse(e.data)));
es.onerror = () => {
// fired on every drop; EventSource is ALREADY retrying. Don't recreate it.
if (es.readyState === EventSource.CLOSED) reallyGiveUp();
};Server читает last-event-id для resume, шлёт : ping как comment-heartbeat, cleanup на close. Client слушает named events; в onerror не recreate — browser уже retrying.
Паттерн SSE: text/event-stream, \n\n terminator, nginx no-buffer, client EventSource без recreate в onerror.
