Skip to content

Long polling #354

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Aug 27, 2020
86 changes: 43 additions & 43 deletions 5-network/10-long-polling/article.md
Original file line number Diff line number Diff line change
@@ -1,96 +1,96 @@
# Long polling
# Sondeo largo

Long polling is the simplest way of having persistent connection with server, that doesn't use any specific protocol like WebSocket or Server Side Events.
Sondeo largo es la forma más sencilla de tener una conexión persistente con el servidor, que no utiliza ningún protocolo específico como WebSocket o Eventos enviados por el servidor.

Being very easy to implement, it's also good enough in a lot of cases.
Al ser muy fácil de implementar, también es suficientemente bueno en muchos casos.

## Regular Polling
## Sondeo regular

The simplest way to get new information from the server is periodic polling. That is, regular requests to the server: "Hello, I'm here, do you have any information for me?". For example, once in 10 seconds.
La forma más sencilla de obtener nueva información del servidor es un sondeo periódico. Es decir, solicitudes regulares al servidor: "Hola, estoy aquí, ¿tienes información para mí?". Por ejemplo, una vez cada 10 segundos.

In response, the server first takes a notice to itself that the client is online, and second - sends a packet of messages it got till that moment.
En respuesta, el servidor primero se da cuenta de que el cliente está en línea, y segundo, envía un paquete de mensajes que recibió hasta ese momento.

That works, but there are downsides:
1. Messages are passed with a delay up to 10 seconds (between requests).
2. Even if there are no messages, the server is bombed with requests every 10 seconds, even if the user switched somewhere else or is asleep. That's quite a load to handle, speaking performance-wise.
Eso funciona, pero hay desventajas:
1. Los mensajes se transmiten con un retraso de hasta 10 segundos (entre solicitudes).
2. Incluso si no hay mensajes, el servidor se bombardea con solicitudes cada 10 segundos, aunque el usuario haya cambiado a otro lugar o esté dormido. Eso es bastante difícil de manejar, hablando en términos de rendimiento.

So, if we're talking about a very small service, the approach may be viable, but generally, it needs an improvement.
Entonces, si hablamos de un servicio muy pequeño, el enfoque puede ser viable, pero en general, necesita una mejora.

## Long polling
## Sondeo largo

So-called "long polling" is a much better way to poll the server.
El llamado "sondeo largo" es una forma mucho mejor de sondear el servidor.

It's also very easy to implement, and delivers messages without delays.
También es muy fácil de implementar y envía mensajes sin demoras.

The flow:
El flujo:

1. A request is sent to the server.
2. The server doesn't close the connection until it has a message to send.
3. When a message appears - the server responds to the request with it.
4. The browser makes a new request immediately.
1. Se envía una solicitud al servidor.
2. El servidor no cierra la conexión hasta que tiene un mensaje para enviar.
3. Cuando aparece un mensaje, el servidor responde a la solicitud con él.
4. El navegador realiza una nueva solicitud de inmediato.

The situation when the browser sent a request and has a pending connection with the server, is standard for this method. Only when a message is delivered, the connection is reestablished.
La situación en la que el navegador envió una solicitud y tiene una conexión pendiente con el servidor, es estándar para este método. Solo cuando se entrega un mensaje, se restablece la conexión.

![](long-polling.svg)

If the connection is lost, because of, say, a network error, the browser immediately sends a new request.
Si se pierde la conexión, por ejemplo, debido a un error de red, el navegador envía inmediatamente una nueva solicitud.

A sketch of client-side `subscribe` function that makes long requests:
Un esquema de la función de suscripción del lado del cliente que realiza solicitudes largas:

```js
async function subscribe() {
let response = await fetch("/subscribe");

if (response.status == 502) {
// Status 502 is a connection timeout error,
// may happen when the connection was pending for too long,
// and the remote server or a proxy closed it
// let's reconnect
// El estado 502 es un error de "tiempo de espera agotado" en la conexión,
// puede suceder cuando la conexión estuvo pendiente durante demasiado tiempo,
// y el servidor remoto o un proxy la cerró
// vamos a reconectarnos
await subscribe();
} else if (response.status != 200) {
// An error - let's show it
// Un error : vamos a mostrarlo
showMessage(response.statusText);
// Reconnect in one second
// Vuelve a conectar en un segundo
await new Promise(resolve => setTimeout(resolve, 1000));
await subscribe();
} else {
// Get and show the message
// Recibe y muestra el mensaje
let message = await response.text();
showMessage(message);
// Call subscribe() again to get the next message
// Llama a subscribe () nuevamente para obtener el siguiente mensaje
await subscribe();
}
}

subscribe();
```

As you can see, `subscribe` function makes a fetch, then waits for the response, handles it and calls itself again.
Como puedes ver, la función `subscribe` realiza una búsqueda, luego espera la respuesta, la maneja y se llama a sí misma nuevamente.

```warn header="Server should be ok with many pending connections"
The server architecture must be able to work with many pending connections.
```warn header="El servidor debería estar bien aún con muchas conexiones pendientes"
La arquitectura del servidor debe poder funcionar con muchas conexiones pendientes.

Certain server architectures run a process per connect. For many connections there will be as many processes, and each process takes a lot of memory. So many connections just consume it all.
Algunas arquitecturas de servidor ejecutan un proceso por conexión. Habrá tantos procesos como conexiones y cada proceso requiere mucha memoria. Demasiadas conexiones la consumirán toda.

That's often the case for backends written in PHP, Ruby languages, but technically isn't a language, but rather implementation issue. Most modern language allow to implement a proper backend, but some of them make it easier than the other.
Este suele ser el caso de los backends escritos en lenguajes PHP, Ruby, pero técnicamente no es un problema del lenguaje sino de implementación. La mayoría de los lenguajes modernos permiten implementar un backend adecuado, pero algunos lo hacen más fácil que otros.

Backends written using Node.js usually don't have such problems.
Los backends escritos con Node.js generalmente no tienen estos problemas.
```

## Demo: a chat
## Demostración: un chat

Here's a demo chat, you can also download it and run locally (if you're familiar with Node.js and can install modules):
Aquí hay un chat de demostración, también puedes descargarlo y ejecutarlo localmente (si estás familiarizado con Node.js y puedes instalar módulos):

[codetabs src="longpoll" height=500]

Browser code is in `browser.js`.
El código del navegador está en `browser.js`.

## Area of usage
## Área de uso

Long polling works great in situations when messages are rare.
El sondeo largo funciona muy bien en situaciones en las que es raro recibir mensajes.

If messages come very often, then the chart of requesting-receiving messages, painted above, becomes saw-like.
Si los mensajes llegan con mucha frecuencia, entonces el gráfico de mensajes solicitados vs recibidos, pintado arriba, se vuelve en forma de sierra.

Every message is a separate request, supplied with headers, authentication overhead, and so on.
Cada mensaje es una solicitud separada, provista de encabezados, sobrecarga de autenticación, etc.

So, in this case, another method is preferred, such as [Websocket](info:websocket) or [Server Sent Events](info:server-sent-events).
Entonces, en este caso, se prefiere otro método, como [Websocket](info:websocket) o [Eventos enviados por el servidor](info:server-sent-events).
16 changes: 8 additions & 8 deletions 5-network/10-long-polling/longpoll.view/browser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Sending messages, a simple POST
// Envío de mensajes, un simple POST
function PublishForm(form, url) {

function sendMessage(message) {
Expand All @@ -18,7 +18,7 @@ function PublishForm(form, url) {
};
}

// Receiving messages with long polling
// Recibir mensajes con sondeo largo
function SubscribePane(elem, url) {

function showMessage(message) {
Expand All @@ -31,18 +31,18 @@ function SubscribePane(elem, url) {
let response = await fetch(url);

if (response.status == 502) {
// Connection timeout
// happens when the connection was pending for too long
// let's reconnect
// El tiempo de conexión expiró
// sucede cuando la conexión estuvo pendiente durante demasiado tiempo
// vamos a reconectarnos
await subscribe();
} else if (response.status != 200) {
// Show Error
// Mostrar Error
showMessage(response.statusText);
// Reconnect in one second
// Volver a conectar en un segundo
await new Promise(resolve => setTimeout(resolve, 1000));
await subscribe();
} else {
// Got message
// Tengo un mensaje
let message = await response.text();
showMessage(message);
await subscribe();
Expand Down
6 changes: 3 additions & 3 deletions 5-network/10-long-polling/longpoll.view/index.html
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<!DOCTYPE html>
<script src="browser.js"></script>

All visitors of this page will see messages of each other.
Todos los visitantes de esta página verán mensajes entre ellos.

<form name="publish">
<input type="text" name="message" />
<input type="submit" value="Send" />
<input type="submit" value="Enviar" />
</form>

<div id="subscribe">
</div>

<script>
new PublishForm(document.forms.publish, 'publish');
// random url parameter to avoid any caching issues
// parámetro de url aleatorio para evitar problemas de almacenamiento en caché
new SubscribePane(document.getElementById('subscribe'), 'subscribe?random=' + Math.random());
</script>
12 changes: 6 additions & 6 deletions 5-network/10-long-polling/longpoll.view/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,28 @@ function publish(message) {
function accept(req, res) {
let urlParsed = url.parse(req.url, true);

// new client wants messages
// El nuevo cliente quiere mensajes
if (urlParsed.pathname == '/subscribe') {
onSubscribe(req, res);
return;
}

// sending a message
// enviando un mensaje
if (urlParsed.pathname == '/publish' && req.method == 'POST') {
// accept POST
// aceptar POST
req.setEncoding('utf8');
let message = '';
req.on('data', function(chunk) {
message += chunk;
}).on('end', function() {
publish(message); // publish it to everyone
publish(message); // publicarlo para todos
res.end("ok");
});

return;
}

// the rest is static
// el resto es estático
fileServer.serve(req, res);

}
Expand All @@ -71,7 +71,7 @@ function close() {

if (!module.parent) {
http.createServer(accept).listen(8080);
console.log('Server running on port 8080');
console.log('Servidor que se ejecuta en el puerto 8080');
} else {
exports.accept = accept;

Expand Down