Real-time web

Post on 22-May-2015

1.196 views 1 download

Transcript of Real-time web

CLOUD COMPUTING.DESARROLLO DE APLICACIONES Y

MINERÍA WEB

Miguel Fernández Fernández miguelff@innova.uniovi.es

Programa de extensión universitariaUniversidad de Oviedo

Web en Tiempo RealXMPP, Websockets, et al.

¿Qué es XMPP?

Extensible Messaging and Presence Protocol

Envío de mensajes en tiempo real

Codificados en XML

Transportados sobre TCP y UDP (media)

antesJabber

http://xmpp.org

¿Por qué XMPP?HTTP

Half-duplex

para la Web

stateless

¿Por qué XMPP?HTTP

Half-duplex

c

snormal polling (AJAX)

c

slong polling (Comet)

para la Web

stateless

¿Por qué XMPP?HTTP

Half-duplex

c

snormal polling (AJAX)

c

slong polling (Comet)

para la Web

stateless

XMPP

Full-duplex

stateful

¿Por qué XMPP?HTTP

Half-duplex

c

snormal polling (AJAX)

c

slong polling (Comet)

para la Web

stateless

XMPP

Full-duplex

stateful

c

sconexión persistente

Arquitectura XMPP

O’REILLY XMPP: The Definitive Guide

Arquitectura XMPP

Web (HTTP) Mail (SMTP)

XMPP

La red XMPP: Entidades

Servidores

Plugins

Componentes

Clientes

Servidores

Enrutan mensajes

Hablan con clientes y otros servidores

FOSS: Ejabberd, Openfire, Tigase

Clientes

Humanos y robots

Protocolo cliente-servidor

Componentes

Extienden la funcionalidad del servidor

Tienen su propia identidad y dirección

Se ejecutan fuera del mismo

Se comunican con un protocolo específico

Ejemplo típico: Multichat

Plugins

Mismo propósito que los componentes

También tienen identidad y dirección

No hay IPC mayor rendimiento

XMPP Stanzas

<stream:stream>

<iq type="get"> <query xmlns="jabber:iq:roster"/> </iq>

<presence/>

<message to="bar@otherside.com" from="foo@oneside.com/adium" type="chat"> <body>Tomamos algo?</body> </message>

<presence type="unavailable"/>

</stream:stream>

XMPP Stanzas

<stream:stream>

<iq type="get"> <query xmlns="jabber:iq:roster"/> </iq>

<presence/>

<message to="bar@otherside.com" from="foo@oneside.com/adium" type="chat"> <body>Tomamos algo?</body> </message>

<presence type="unavailable"/>

</stream:stream>

XMPP Stanzas

<stream:stream>

<iq type="get"> <query xmlns="jabber:iq:roster"/> </iq>

<presence/>

<message to="bar@otherside.com" from="foo@oneside.com/adium" type="chat"> <body>Tomamos algo?</body> </message>

<presence type="unavailable"/>

</stream:stream>

Dame mis contactos

XMPP Stanzas

<stream:stream>

<iq type="get"> <query xmlns="jabber:iq:roster"/> </iq>

<presence/>

<message to="bar@otherside.com" from="foo@oneside.com/adium" type="chat"> <body>Tomamos algo?</body> </message>

<presence type="unavailable"/>

</stream:stream>

Dame mis contactos

Estoy online

XMPP Stanzas

<stream:stream>

<iq type="get"> <query xmlns="jabber:iq:roster"/> </iq>

<presence/>

<message to="bar@otherside.com" from="foo@oneside.com/adium" type="chat"> <body>Tomamos algo?</body> </message>

<presence type="unavailable"/>

</stream:stream>

Dame mis contactos

Estoy online

Dile a bar que si tomamos algo

XMPP Stanzas

<stream:stream>

<iq type="get"> <query xmlns="jabber:iq:roster"/> </iq>

<presence/>

<message to="bar@otherside.com" from="foo@oneside.com/adium" type="chat"> <body>Tomamos algo?</body> </message>

<presence type="unavailable"/>

</stream:stream>

Dame mis contactos

Estoy online

Dile a bar que si tomamos algoYa no estoy disponible

Tiempo real en La Web

Acercando XMPP a la Web

Pre HTML 5

Comunicación basada en HTTP

Bidirectional-Streams over synchronous HTTP

AJAX & Long Polling

AJAX & Long Polling

setInterval(function(){ // pedimos cada 500 milisegundos esperando cambio $.ajax({ url: '/my/page', success: function(data){} });}, 500);

function load(){ $.ajax({ url: '/my/page', success: function(){ // abrimos la conexión durante 20 segundos }, complete: load, timeout: 20000 });}

AJAX (muestreo frecuente) Comet (Long Polling)

Latencia (200ms/petición)

Muchas peticiones no recogeran cambios

Se genera mucho tráfico

Reducción dramática de latencia

Mucho más eficiente

BOSH, XMPP sobre HTTP

http://xmpp.org/extensions/xep-0206.html

Flujos bidireccionales sobre HTTP síncrono

Usa pares de petición-respuesta para simular

Requiere de un proxy que dirija los stanzas al servidor XMPP

HTTP/1.1 200 OKContent-Type: text/xml; charset=utf-8Content-Length: 483

<body xmpp:version='1.0' authid='ServerStreamID' xmlns='http://jabber.org/protocol/httpbind' xmlns:xmpp='urn:xmpp:xbosh' xmlns:stream='http://etherx.jabber.org/streams'> <stream:features> <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <mechanism>SCRAM-SHA-1</mechanism> <mechanism>PLAIN</mechanism> </mechanisms> </stream:features></body>

HTML5 WebsocketsHTML 5

WebSocketsTo enable Web applications to maintain bidirectional

communications with server-side processes, this specification introduces the WebSocket interface.

Gecko 2.0b4 (24/08/2010) (Firefox 4 Nighties)Webkit 333 (Safari 4, Chrome >4)Soportado en:

HTML5 WebsocketsHTML 5

WebSocketsTo enable Web applications to maintain bidirectional

communications with server-side processes, this specification introduces the WebSocket interface.

Gecko 2.0b4 (24/08/2010) (Firefox 4 Nighties)Webkit 333 (Safari 4, Chrome >4)Soportado en:

c

sconexión persistente

El contrato Websocket[Constructor(in DOMString url, in optional DOMString protocols)][Constructor(in DOMString url, in optional DOMString[] protocols)]interface WebSocket { readonly attribute DOMString url;

// ready state const unsigned short CONNECTING = 0; const unsigned short OPEN = 1; const unsigned short CLOSING = 2; const unsigned short CLOSED = 3; readonly attribute unsigned short readyState; readonly attribute unsigned long bufferedAmount;

// networking attribute Function onopen; attribute Function onmessage; attribute Function onerror; attribute Function onclose; readonly attribute DOMString protocol; void send(in DOMString data); void close();};WebSocket implements EventTarget;

estado de la conexión

Recepción de eventos

Envío de mensajes

ws://services.com/service

Web en tiempo real con Websockets

Event Machine

http://rubyeventmachine.com/

Framework I/O dirigida por eventos

Corre sobre ruby

à-la node.js (javascript) y twisted (python)

Implementa el patrón Reactor

Muy útil para crear aplicaciones servidor

eventmachine (0.12.10)eventmachine-websocket (0.1.0)

Nuestro cliente<html> <head> <script src='jquery.min.js'></script> <script> function WebSocketAdapter(url){ this.ws=new WebSocket(url); this.ws.onmessage = function(evt) {$("#msg").append("<p>"+evt.data+"</p>");}; this.ws.onclose = function() { alert("socket cerrado"); }; this.ws.onopen = function() { alert("conectado..."); }; this.send=function(msg) {this.ws.send(msg);} } var ws; $(document).ready(function(){ ws=new WebSocketAdapter("ws://localhost:8080/"); }); </script> </head> <body> <form> Enviar al servidor: <input id="texto" type="text" value="hola mundo!"></input> <input id="enviar" type="button" value="enviar" onclick="ws.send($('#texto').val())"/> </form> <div id="msg"></div> </body></html>

Nuestro cliente<html> <head> <script src='jquery.min.js'></script> <script> function WebSocketAdapter(url){ this.ws=new WebSocket(url); this.ws.onmessage = function(evt) {$("#msg").append("<p>"+evt.data+"</p>");}; this.ws.onclose = function() { alert("socket cerrado"); }; this.ws.onopen = function() { alert("conectado..."); }; this.send=function(msg) {this.ws.send(msg);} } var ws; $(document).ready(function(){ ws=new WebSocketAdapter("ws://localhost:8080/"); }); </script> </head> <body> <form> Enviar al servidor: <input id="texto" type="text" value="hola mundo!"></input> <input id="enviar" type="button" value="enviar" onclick="ws.send($('#texto').val())"/> </form> <div id="msg"></div> </body></html>

Nuestro cliente<html> <head> <script src='jquery.min.js'></script> <script> function WebSocketAdapter(url){ this.ws=new WebSocket(url); this.ws.onmessage = function(evt) {$("#msg").append("<p>"+evt.data+"</p>");}; this.ws.onclose = function() { alert("socket cerrado"); }; this.ws.onopen = function() { alert("conectado..."); }; this.send=function(msg) {this.ws.send(msg);} } var ws; $(document).ready(function(){ ws=new WebSocketAdapter("ws://localhost:8080/"); }); </script> </head> <body> <form> Enviar al servidor: <input id="texto" type="text" value="hola mundo!"></input> <input id="enviar" type="button" value="enviar" onclick="ws.send($('#texto').val())"/> </form> <div id="msg"></div> </body></html>

Nuestro cliente<html> <head> <script src='jquery.min.js'></script> <script> function WebSocketAdapter(url){ this.ws=new WebSocket(url); this.ws.onmessage = function(evt) {$("#msg").append("<p>"+evt.data+"</p>");}; this.ws.onclose = function() { alert("socket cerrado"); }; this.ws.onopen = function() { alert("conectado..."); }; this.send=function(msg) {this.ws.send(msg);} } var ws; $(document).ready(function(){ ws=new WebSocketAdapter("ws://localhost:8080/"); }); </script> </head> <body> <form> Enviar al servidor: <input id="texto" type="text" value="hola mundo!"></input> <input id="enviar" type="button" value="enviar" onclick="ws.send($('#texto').val())"/> </form> <div id="msg"></div> </body></html>

Nuestro cliente<html> <head> <script src='jquery.min.js'></script> <script> function WebSocketAdapter(url){ this.ws=new WebSocket(url); this.ws.onmessage = function(evt) {$("#msg").append("<p>"+evt.data+"</p>");}; this.ws.onclose = function() { alert("socket cerrado"); }; this.ws.onopen = function() { alert("conectado..."); }; this.send=function(msg) {this.ws.send(msg);} } var ws; $(document).ready(function(){ ws=new WebSocketAdapter("ws://localhost:8080/"); }); </script> </head> <body> <form> Enviar al servidor: <input id="texto" type="text" value="hola mundo!"></input> <input id="enviar" type="button" value="enviar" onclick="ws.send($('#texto').val())"/> </form> <div id="msg"></div> </body></html>

Nuestro cliente<html> <head> <script src='jquery.min.js'></script> <script> function WebSocketAdapter(url){ this.ws=new WebSocket(url); this.ws.onmessage = function(evt) {$("#msg").append("<p>"+evt.data+"</p>");}; this.ws.onclose = function() { alert("socket cerrado"); }; this.ws.onopen = function() { alert("conectado..."); }; this.send=function(msg) {this.ws.send(msg);} } var ws; $(document).ready(function(){ ws=new WebSocketAdapter("ws://localhost:8080/"); }); </script> </head> <body> <form> Enviar al servidor: <input id="texto" type="text" value="hola mundo!"></input> <input id="enviar" type="button" value="enviar" onclick="ws.send($('#texto').val())"/> </form> <div id="msg"></div> </body></html>

Nuestro cliente<html> <head> <script src='jquery.min.js'></script> <script> function WebSocketAdapter(url){ this.ws=new WebSocket(url); this.ws.onmessage = function(evt) {$("#msg").append("<p>"+evt.data+"</p>");}; this.ws.onclose = function() { alert("socket cerrado"); }; this.ws.onopen = function() { alert("conectado..."); }; this.send=function(msg) {this.ws.send(msg);} } var ws; $(document).ready(function(){ ws=new WebSocketAdapter("ws://localhost:8080/"); }); </script> </head> <body> <form> Enviar al servidor: <input id="texto" type="text" value="hola mundo!"></input> <input id="enviar" type="button" value="enviar" onclick="ws.send($('#texto').val())"/> </form> <div id="msg"></div> </body></html>

Echo (single client)

require 'rubygems'require 'eventmachine-websocket'

EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |con| con.on_open { con.send "Cliente conectado"} con.on_message { |msg| con.send msg.reverse } con.on_close { puts "Cliente desconectado" }end

Multichat en 23LOCrequire 'rubygems'require 'eventmachine-websocket'

connections=[]indexes={}

EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |con|

con.on_open do indexes[con]=connections.size+1 con.send "<p class=\"highlight\">Eres el cliente #{indexes[con]}<p>" connections.each{ |c| c.send "<p class=\"highlight\">El cliente #{indexes[con]} ha entrado en la sala<p>" } connections << con end con.on_message do |msg| connections.each{ |c| c.send "<p><span class=\"cliente\">Cliente #{indexes[con]}:</span> #{msg}</p>" } end con.on_close do c.send "<p class=\"highlight\">Has abandonado la sala</p>" connections.delete con indexes.delete con endend

Conclusiones

• Hasta la aparición de HTML5, XMPP tenía unas expectativas muy altas como alternativa a Comet.

• Sin embargo, se han cancelado muchos servicios XMPP para el consumo de datos en tiempo real (Twitter firehose API)

•Websocket se presenta como una alternativa más simple y elegante para la implementación de servicios Web de tiempo real

• XMPP no pierde fuerza para mensajería instantánea

Bilbiografía

Gracias

CLOUD COMPUTING.DESARROLLO DE APLICACIONES Y

MINERÍA WEB

Miguel Fernández Fernández miguelff@innova.uniovi.es

Programa de extensión universitariaUniversidad de Oviedo