ÐлиннÑе опÑоÑÑ â ÑÑо ÑамÑй пÑоÑÑой ÑпоÑоб поддеÑживаÑÑ Ð¿Ð¾ÑÑоÑнное Ñоединение Ñ ÑеÑвеÑом, не иÑполÑзÑÑ Ð¿Ñи ÑÑом Ð½Ð¸ÐºÐ°ÐºÐ¸Ñ ÑпеÑиÑиÑеÑÐºÐ¸Ñ Ð¿ÑоÑоколов (Ñипа WebSocket или Server Sent Events).
Ðго оÑÐµÐ½Ñ Ð»ÐµÐ³ÐºÐ¾ ÑеализоваÑÑ, и он Ñ Ð¾ÑоÑо Ð¿Ð¾Ð´Ñ Ð¾Ð´Ð¸Ñ Ð´Ð»Ñ Ð¼Ð½Ð¾Ð³Ð¸Ñ Ð·Ð°Ð´Ð°Ñ.
ЧаÑÑÑе опÑоÑÑ
СамÑй пÑоÑÑой ÑпоÑоб полÑÑаÑÑ Ð½Ð¾Ð²ÑÑ Ð¸Ð½ÑоÑмаÑÐ¸Ñ Ð¾Ñ ÑеÑвеÑа â пеÑиодиÑеÑкий опÑоÑ. То еÑÑÑ, ÑегÑлÑÑнÑе запÑоÑÑ Ð½Ð° ÑеÑÐ²ÐµÑ Ð²Ð¸Ð´Ð°: «ÐÑивеÑ, Ñ Ð·Ð´ÐµÑÑ, Ñ Ð²Ð°Ñ ÐµÑÑÑ ÐºÐ°ÐºÐ°Ñ-нибÑÐ´Ñ Ð¸Ð½ÑоÑмаÑÐ¸Ñ Ð´Ð»Ñ Ð¼ÐµÐ½Ñ?». ÐапÑимеÑ, Ñаз в 10 ÑекÑнд.
РоÑÐ²ÐµÑ ÑеÑвеÑ, во-пеÑвÑÑ , помеÑÐ°ÐµÑ Ñ ÑебÑ, ÑÑо ÐºÐ»Ð¸ÐµÐ½Ñ Ð¾Ð½Ð»Ð°Ð¹Ð½, а во-вÑоÑÑÑ Ð¿Ð¾ÑÑÐ»Ð°ÐµÑ Ð²ÐµÑÑ Ð¿Ð°ÐºÐµÑ ÑообÑений, накопивÑÐ¸Ñ ÑÑ Ðº Ð´Ð°Ð½Ð½Ð¾Ð¼Ñ Ð¼Ð¾Ð¼ÐµÐ½ÑÑ.
ÐÑо ÑабоÑаеÑ, но еÑÑÑ Ð¸ недоÑÑаÑки:
- СообÑÐµÐ½Ð¸Ñ Ð¿ÐµÑедаÑÑÑÑ Ñ Ð·Ð°Ð´ÐµÑжкой до 10 ÑекÑнд (Ð¼ÐµÐ¶Ð´Ñ Ð·Ð°Ð¿ÑоÑами).
- Ðаже еÑли ÑообÑений неÑ, ÑеÑÐ²ÐµÑ Â«Ð°ÑакÑеÑÑÑ» запÑоÑами каждÑе 10 ÑекÑнд, даже еÑли полÑзоваÑÐµÐ»Ñ Ð¿ÐµÑеклÑÑилÑÑ ÐºÑда-нибÑÐ´Ñ Ð¸Ð»Ð¸ ÑпиÑ. С ÑоÑки зÑÐµÐ½Ð¸Ñ Ð¿ÑоизводиÑелÑноÑÑи, ÑÑо доволÑно болÑÑÐ°Ñ Ð½Ð°Ð³ÑÑзка.
Так ÑÑо, еÑли ÑеÑÑ Ð¸Ð´ÑÑ Ð¾Ð± оÑÐµÐ½Ñ Ð¼Ð°Ð»ÐµÐ½Ñком ÑеÑвиÑе, Ð¿Ð¾Ð´Ñ Ð¾Ð´ Ð¼Ð¾Ð¶ÐµÑ Ð¾ÐºÐ°Ð·Ð°ÑÑÑÑ Ð¶Ð¸Ð·Ð½ÐµÑпоÑобнÑм, но в Ñелом он нÑждаеÑÑÑ Ð² ÑлÑÑÑении.
ÐлиннÑе опÑоÑÑ
«ÐлиннÑе опÑоÑÑ» â гоÑаздо лÑÑÑий ÑпоÑоб взаимодейÑÑÐ²Ð¸Ñ Ñ ÑеÑвеÑом.
Ðни Ñакже оÑÐµÐ½Ñ Ð¿ÑоÑÑÑ Ð² ÑеализаÑии, и ÑообÑÐµÐ½Ð¸Ñ Ð´Ð¾ÑÑавлÑÑÑÑÑ Ð±ÐµÐ· задеÑжек.
Ðак ÑÑо пÑоиÑÑ Ð¾Ð´Ð¸Ñ:
- ÐапÑÐ¾Ñ Ð¾ÑпÑавлÑеÑÑÑ Ð½Ð° ÑеÑвеÑ.
- СеÑÐ²ÐµÑ Ð½Ðµ закÑÑÐ²Ð°ÐµÑ Ñоединение, пока Ñ Ð½ÐµÐ³Ð¾ не Ð²Ð¾Ð·Ð½Ð¸ÐºÐ½ÐµÑ ÑообÑение Ð´Ð»Ñ Ð¾ÑÑÑлки.
- Ðогда поÑвлÑеÑÑÑ ÑообÑение â ÑеÑÐ²ÐµÑ Ð¾ÑвеÑÐ°ÐµÑ Ð½Ð° запÑоÑ, поÑÑÐ»Ð°Ñ ÐµÐ³Ð¾.
- ÐÑаÑÐ·ÐµÑ Ð½ÐµÐ¼ÐµÐ´Ð»ÐµÐ½Ð½Ð¾ Ð´ÐµÐ»Ð°ÐµÑ Ð½Ð¾Ð²Ñй запÑоÑ.
ÐÐ»Ñ Ð´Ð°Ð½Ð½Ð¾Ð³Ð¾ меÑода ÑиÑÑаÑиÑ, когда бÑаÑÐ·ÐµÑ Ð¾ÑпÑавил запÑÐ¾Ñ Ð¸ ÑдеÑÐ¶Ð¸Ð²Ð°ÐµÑ Ñоединение Ñ ÑеÑвеÑом в ожидании оÑвеÑа, ÑвлÑеÑÑÑ ÑÑандаÑÑной. Соединение пÑеÑÑваеÑÑÑ ÑолÑко доÑÑавкой ÑообÑений.
ÐÑли Ñоединение бÑÐ´ÐµÑ Ð¿Ð¾ÑеÑÑно, Ñкажем, из-за ÑеÑевой оÑибки, бÑаÑÐ·ÐµÑ Ð½ÐµÐ¼ÐµÐ´Ð»ÐµÐ½Ð½Ð¾ поÑÑÐ»Ð°ÐµÑ Ð½Ð¾Ð²Ñй запÑоÑ.
ÐÑимеÑнÑй код клиенÑÑкой ÑÑнкÑии subscribe, коÑоÑÐ°Ñ ÑеализÑÐµÑ Ð´Ð»Ð¸Ð½Ð½Ñе опÑоÑÑ:
async function subscribe() {
let response = await fetch("/subscribe");
if (response.status == 502) {
// СÑаÑÑÑ 502 - ÑÑо ÑаймаÑÑ ÑоединениÑ;
// возможен, когда Ñоединение ожидало ÑлиÑком долго
// и ÑеÑÐ²ÐµÑ (или пÑомежÑÑоÑнÑй пÑокÑи) закÑÑл его
// давайÑе воÑÑÑановим ÑвÑзÑ
await subscribe();
} else if (response.status != 200) {
// ÐакаÑ-Ñо оÑибка, покажем еÑ
showMessage(response.statusText);
// ÐодклÑÑимÑÑ Ñнова ÑеÑез ÑекÑндÑ.
await new Promise(resolve => setTimeout(resolve, 1000));
await subscribe();
} else {
// ÐолÑÑим и покажем ÑообÑение
let message = await response.text();
showMessage(message);
// Ð Ñнова вÑзовем subscribe() Ð´Ð»Ñ Ð¿Ð¾Ð»ÑÑÐµÐ½Ð¸Ñ ÑледÑÑÑего ÑообÑениÑ
await subscribe();
}
}
subscribe();
ФÑнкÑÐ¸Ñ subscribe() Ð´ÐµÐ»Ð°ÐµÑ Ð·Ð°Ð¿ÑоÑ, заÑем Ð¾Ð¶Ð¸Ð´Ð°ÐµÑ Ð¾ÑвеÑ, обÑабаÑÑÐ²Ð°ÐµÑ ÐµÐ³Ð¾ и Ñнова вÑзÑÐ²Ð°ÐµÑ Ñама ÑебÑ.
ÐÑÑ Ð¸ÑекÑÑÑа ÑеÑвеÑа должна бÑÑÑ ÑпоÑобна ÑабоÑаÑÑ Ñо многими ожидаÑÑими подклÑÑениÑми.
ÐекоÑоÑÑе ÑеÑвеÑнÑе аÑÑ Ð¸ÑекÑÑÑÑ Ð·Ð°Ð¿ÑÑкаÑÑ Ð¾ÑделÑнÑй пÑоÑеÑÑ Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ ÑоединениÑ. ÐÐ»Ñ Ð±Ð¾Ð»ÑÑого колиÑеÑÑва Ñоединений бÑÐ´ÐµÑ ÑÑолÑко же пÑоÑеÑÑов, и каждÑй пÑоÑеÑÑ Ð·Ð°Ð½Ð¸Ð¼Ð°ÐµÑ Ð·Ð½Ð°ÑиÑелÑнÑй обÑÑм памÑÑи. Так много Ñоединений пÑоÑÑо поглоÑÑÑ Ð²ÑÑ Ð¿Ð°Ð¼ÑÑÑ.
ЧаÑÑо ÑÐ°ÐºÐ°Ñ Ð¿Ñоблема Ð²Ð¾Ð·Ð½Ð¸ÐºÐ°ÐµÑ Ñ Ð±Ñкендом, напиÑаннÑми на PHP или Ruby, но ÑÐµÑ Ð½Ð¸ÑеÑки дело не в ÑзÑке, а в ÑеализаÑии. Ðа болÑÑинÑÑве ÑовÑеменнÑÑ ÑзÑков можно напиÑаÑÑ Ð¿Ð¾Ð´Ñ Ð¾Ð´ÑÑий ÑеÑвеÑ, но на некоÑоÑÑÑ ÑÑо пÑоÑе ÑделаÑÑ.
ÐÑкендÑ, напиÑаннÑе Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ Node.js, обÑÑно не имеÑÑ ÑÐ°ÐºÐ¸Ñ Ð¿Ñоблем.
Ðемо: ÑаÑ
ÐÐ¾Ñ Ð´ÐµÐ¼Ð¾-ÑаÑ, Ð²Ñ Ñакже можеÑе ÑкаÑаÑÑ ÐµÐ³Ð¾ и запÑÑÑиÑÑ Ð»Ð¾ÐºÐ°Ð»Ñно (еÑли вам знаком Node.js и можеÑе поÑÑавиÑÑ Ð¼Ð¾Ð´Ñли):
// ÐÑпÑавка ÑообÑений, пÑоÑÑой POST
function PublishForm(form, url) {
function sendMessage(message) {
fetch(url, {
method: 'POST',
body: message
});
}
form.onsubmit = function() {
let message = form.message.value;
if (message) {
form.message.value = '';
sendMessage(message);
}
return false;
};
}
// ÐолÑÑение ÑообÑений пÑÑÑм длинного опÑоÑа
function SubscribePane(elem, url) {
function showMessage(message) {
let messageElem = document.createElement('div');
messageElem.append(message);
elem.append(messageElem);
}
async function subscribe() {
let response = await fetch(url);
if (response.status == 502) {
// ТаймаÑÑ Ð¿Ð¾Ð´ÐºÐ»ÑÑениÑ
// ÑлÑÑаеÑÑÑ, когда Ñоединение ждало ÑлиÑком долго.
// давайÑе воÑÑÑановим ÑвÑзÑ
await subscribe();
} else if (response.status != 200) {
// ÐоказаÑÑ Ð¾ÑибкÑ
showMessage(response.statusText);
// ÐодклÑÑиÑÑÑÑ Ñнова ÑеÑез ÑекÑндÑ.
await new Promise(resolve => setTimeout(resolve, 1000));
await subscribe();
} else {
// ÐолÑÑиÑÑ ÑообÑение
let message = await response.text();
showMessage(message);
await subscribe();
}
}
subscribe();
}let http = require('http');
let url = require('url');
let querystring = require('querystring');
let static = require('node-static');
let fileServer = new static.Server('.');
let subscribers = Object.create(null);
function onSubscribe(req, res) {
let id = Math.random();
res.setHeader('Content-Type', 'text/plain;charset=utf-8');
res.setHeader("Cache-Control", "no-cache, must-revalidate");
subscribers[id] = res;
req.on('close', function() {
delete subscribers[id];
});
}
function publish(message) {
for (let id in subscribers) {
let res = subscribers[id];
res.end(message);
}
subscribers = Object.create(null);
}
function accept(req, res) {
let urlParsed = url.parse(req.url, true);
// новÑй ÐºÐ»Ð¸ÐµÐ½Ñ Ñ
оÑÐµÑ Ð¿Ð¾Ð»ÑÑаÑÑ ÑообÑениÑ
if (urlParsed.pathname == '/subscribe') {
onSubscribe(req, res);
return;
}
// оÑпÑавка ÑообÑениÑ
if (urlParsed.pathname == '/publish' && req.method == 'POST') {
// пÑинÑÑÑ POST
req.setEncoding('utf8');
let message = '';
req.on('data', function(chunk) {
message += chunk;
}).on('end', function() {
publish(message); // опÑбликоваÑÑ Ð´Ð»Ñ Ð²ÑеÑ
res.end("ok");
});
return;
}
// оÑÑалÑное - ÑÑаÑика
fileServer.serve(req, res);
}
function close() {
for (let id in subscribers) {
let res = subscribers[id];
res.end();
}
}
// -----------------------------------
if (!module.parent) {
http.createServer(accept).listen(8080);
console.log('Server running on port 8080');
} else {
exports.accept = accept;
if (process.send) {
process.on('message', (msg) => {
if (msg === 'shutdown') {
close();
}
});
}
process.on('SIGINT', close);
}<!DOCTYPE html>
<script src="browser.js"></script>
ÐÑе поÑеÑиÑели ÑÑой ÑÑÑаниÑÑ Ð±ÑдÑÑ Ð²Ð¸Ð´ÐµÑÑ ÑообÑÐµÐ½Ð¸Ñ Ð´ÑÑг дÑÑга.
<form name="publish">
<input type="text" name="message" />
<input type="submit" value="Send" />
</form>
<div id="subscribe">
</div>
<script>
new PublishForm(document.forms.publish, 'publish');
// ÑлÑÑайнÑй url, ÑÑÐ¾Ð±Ñ Ð¸Ð·Ð±ÐµÐ¶Ð°ÑÑ Ð¿Ñоблем Ñ ÐºÐµÑиÑованием.
new SubscribePane(document.getElementById('subscribe'), 'subscribe?random=' + Math.random());
</script>ÐÑаÑзеÑнÑй код наÑ
одиÑÑÑ Ð² browser.js.
ÐблаÑÑÑ Ð¿ÑименениÑ
ÐлиннÑе опÑоÑÑ Ð¿ÑекÑаÑно ÑабоÑаÑÑ, когда ÑообÑÐµÐ½Ð¸Ñ Ð¿ÑÐ¸Ñ Ð¾Ð´ÑÑ Ñедко.
ÐÑли ÑообÑÐµÐ½Ð¸Ñ Ð¿ÑÐ¸Ñ Ð¾Ð´ÑÑ Ð¾ÑÐµÐ½Ñ ÑаÑÑо, Ñо ÑÑ ÐµÐ¼Ð° пÑиÑма-оÑпÑавки ÑообÑений, пÑиведÑÐ½Ð½Ð°Ñ Ð²ÑÑе, ÑÑановиÑÑÑ Ð¿Ð¾Ñ Ð¾Ð¶ÐµÐ¹ на «пилÑ».
Ðаждое ÑообÑение â ÑÑо оÑделÑнÑй запÑоÑ, Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ°Ð¼Ð¸, авÑоÑизаÑией и Ñак далее.
ÐоÑÑÐ¾Ð¼Ñ Ð² ÑÑом ÑлÑÑае пÑедпоÑÑиÑелÑнее иÑполÑзоваÑÑ Ð´ÑÑгой меÑод, Ñакой как Websocket или Server Sent Events.

ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)