Realtime updates with WebSocket
- Patricio Mac Adden @maxawen / patriciomacadden
Agenda
- Realtime updates: alternativas
- WebSockets
- Caso de éxito
- Ejemplo: em-websocket + Javascript API
- Ovación de pie (si todo sale bien!)
Alternativas
- Polling
- Faye (pub/sub messaging system based on the Bayeux protocol)
- SSE (Server-Sent Events, Event Source API)
- WebSocket
WebSocket
Características
- Define una conexión full-duplex que opera sobre un sólo socket
- Reduce el tráfico y la latencia de la red comparado con polling
- Atraviesa firewalls y proxies
- Soportado por todos los navegadores modernos
Protocolo
- Diseñado para funcionar en la infraestructura web existente
- Una conexión WebSocket comienza su ciclo de vida como una conexión HTTP
- El cambio del protocolo HTTP a WebSocket es llamado Handshake
Handshake
El cliente envía un request al servidor indicando que quiere cambiar protocolos de HTTP a WebSocket mediante el header Upgrade.
GET ws://echo.websocket.org/?encoding=text HTTP/1.1
Origin: http://websocket.org
Cookie: __utma=99as
Connection: Upgrade
Host: echo.websocket.org
Sec-WebSocket-Key: uRovscZjNol/umbTt5uKmw==
Upgrade: websocket
Sec-WebSocket-Version: 13
Si el servidor entiende el protocolo WebSocket, acepta cambiar el protocolo mediante el header Upgrade.
HTTP/1.1 101 WebSocket Protocol Handshake
Date: Fri, 10 Feb 2012 17:38:18 GMT
Connection: Upgrade
Server: Kaazing Gateway
Upgrade: WebSocket
Access-Control-Allow-Origin: http://websocket.org
Access-Control-Allow-Credentials: true
Sec-WebSocket-Accept: rLHCkw/SKsO9GAH/ZSFhBATDKrU=
Access-Control-Allow-Headers: content-type
En este momento la conexión HTTP es reemplazada por la conexión WebSocket en la misma conexión TCP/IP subyacente.
Caso de éxito
Motivación
- Cliente proveedor de digital signage
- Dispositivos de digital signage ya desarrollados, algunos detrás de firewalls
- Dispositivos de digital signage por desarrollar
- Desarrollar plataforma de administración de advertising
- Desarrollar plataforma de comunicación entre plataforma de administración y dispositivos
Soluciones
- Plataforma de comunicación: WebSocket
- Plataforma de administración: Realtime updates con WebSocket
Realtime updates con WebSocket
- Dos servidores WebSocket (EventMachine)
- 1 para la plataforma de comunicación
- 1 para la plataforma de administración (realtime updates en el browser)
- Redis para pub/sub
- Los dispositivos se conectan a la platarforma de comunicación
- Los browsers (usuarios) se conectan a la platarforma de administración
Un caso puntual: actualizar una página cuando un dispositivo se conecta
- El dispositivo se conecta a la plataforma de comunicación
- Se publica en Redis, dando aviso a la plataforma de administración
- La plataforma de administración envía un mensaje a los browsers, que actualizan la página dando cuenta de la conexión del dispositivo
Ejemplo
Aplicación que envía un mensaje al servidor WebSocket y recibe una respuesta, la cual imprime en la pantalla.
Antes de empezar
-
Gemas
- em-websocket: para el servidor de WebSocket
- rack: para la web app
- Gemfile
source 'https://rubygems.org'
gem 'em-websocket'
gem 'rack'
Servidor WebSocket
require 'bundler/setup'
require 'em-websocket'
EM.run do
EM::WebSocket.start host: '0.0.0.0', port: '8080' do |ws|
ws.onmessage do |message|
ws.send "[#{Time.now.strftime '%D %T'}] #{message}"
end
end
end
Web app
require 'bundler/setup'
require 'rack'
app = ->(env) do
tpl = File.read File.join(__dir__, 'views', 'index.html')
Rack::Response.new [tpl], 200, {}
end
run app
Template: WebSocket Javascript API
<!DOCTYPE html>
<html>
<head>
<title>WebSocket demo</title>
</head>
<body>
<h1>WebSocket demo</h1>
<form action="javascript:;" id="send-message">
<input type="text" id="message">
<input type="submit">
</form>
<ul id="messages"></ul>
<script>
function appendMessage(message) {
var element = document.createElement('li');
element.innerText = message;
var messages = document.querySelector('#messages');
messages.appendChild(element);
}
document.addEventListener('DOMContentLoaded', function() {
ws = new WebSocket('ws://localhost:8080');
ws.onerror = function(event) {
appendMessage(':(');
}
ws.onmessage = function(event) {
appendMessage(event.data);
};
var form = document.querySelector('#send-message');
form.onsubmit = function(event) {
event.preventDefault();
var message = document.querySelector('#message');
ws.send(message.value);
}
});
</script>
</body>
</html>