Popular prezis
Real Time Web and Basketball Marathon
Pet Project
O que é Maratona de basquete?
Tecnologias utilizadas
Arquitetura de uma app em tempo real
Fluxo PubSub
Desafios da implementação
Demo
Luís Cipriani
Twitter: @lfcipriani
lfcipriani@talleye.com
Bs Computer Science + a lot of other stuff
30 horas
40 minutos
12 jogadores
200 jogadores
Basquete
Maratona de Basquete
Cá entre nós... quem é o louco que fica mantendo o placar disso???
Não seria legal se tivesse uma aplicação que fizesse isso???
1997: primeira versão feita em Basic
2000 até 2008: Placar em Delphi
2009: ruby, rails, xmpp, erlang, javascript, ajax, html, google gears
www.maratonadebasquete.com.br/marathons/1
Browser
Scoutista/publisher
JQuery UI
Ajax Manager
JQPlot
Sparklines
JQuery Countdown
Data Switcher
Browser
Subscriber
JQuery UI
Ajax Manager
JQPlot
Sparklines
JQuery Countdown
PubSub client
Browser
Subscriber
JQuery UI
Ajax Manager
JQPlot
Sparklines
JQuery Countdown
PubSub client
Reverse Proxy
mod_rails
(Firefox, IE6/7/8,
Chrome, Opera, ...)
(Firefox, IE6/7/8, Safari
Chrome, Opera, ...)
(Firefox, IE6/7/8, Safari
Chrome, Opera, ...)
HTTP-bind
public
folder
mod_pubsub
Marathon-Bot
sync
controllers
memcached
shared models
TCP
TCP
XMPP4R
Daemon-kit
Eventmachine
Activerecord
REXML
XMPP4R/Pubsub
Twitter gem
250 linhas de código
Componente mais simples
Renderiza front-end
Realiza sincronização com publishers e subscribers
Sincronização completa com publishers
Sincronização sucinta com subscribers
...
,
<iq type='set' from='marathon-bot@zaphod'
to='pubsub.zaphod' >
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<publish node='/home/zaphod/marathon-bot/events'>
<item>
<event marathon='1'
timestamp='1255059679512'
type='action'
id='1255059679512'>
<storage method='add' sync='local'/>
<team>2</team><action>1</action>
</event>
</item>
</publish>
</pubsub>
</iq>
Stanzas
<presence from='marathon-bot@zaphod' />
<message from='romeo@montague.lit' to='juliet@capulet.com' id='msg_1'>
<body>Quer tc?</body>
</message>
<iq type="set" id="1-1" from="wave.initech-corp.com" to="wave.acmewave.com">
<pubsub xmlns="http://jabber.org/protocol/pubsub">
<publish node="wavelet">
<item>
<submit-request
xmlns="http://waveprotocol.org/protocol/0.2/waveserver">
<delta wavelet-name="acmewave.com/initech-corp.com!a/b">
<![CDATA[CiA...NvbQ==] ]>
</delta>
</submit-request>
</item>
</publish>
</pubsub>
</iq>
XMPP
Protocolo baseado em XML
Conexões persistentes
Presença
Membros/Rosters
Extensível
Possui comunidade bastante ativa
Ejabberd
Servidor XMPP implementado em Erlang
Robusto e escalável
Bom desempenho em benchmarks
Implementa módulos importantes
Extensível via módulos Erlang
Fácil de configurar e utilizar (web admin)
Comet
Termo guarda-chuva para técnicas de server push
Exemplos:
Streaming via iframe escondido
Xhr com mime multipart/x-mixed-replace
Ajax com long polling
Script tag com long polling
HTML5 websockets
E mais uma tonelada de variações sobre essas idéias
Strophe
Client XMPP escrito em Javascript
Implementa o padrão BOSH (XEP-0124)
Faz long polling
A versão utilizada ainda não faz cross-domain requests
A nova versão usa um componente Flash para isso
Incrivelmente fácil de usar
Compatível com Firefox, Chrome, Opera, Safari, IE6,7,8, etc
Google Gears
Compatível com vários browsers
No placar, foi utilizado LocalServer e LocalStorage
Usado somente no scoutista/publisher
Evita perda de dados
Aumenta desempenho pois menos dados são trafegados na rede
LocalStorage usa sqlite
Fácil de usar
Workflow PubSub
O que acontece quando alguém faz uma cesta?
SQL
<iq from='scoutista@zaphod/1' to='marathon-bot@zaphod/server'
id='6155:webclient' xmlns='jabber:client'>
<query xmlns='http://cipriani/game/capture'>
<event marathon='1' timestamp='1255052864229' type='action' id='1255052864229'>
<storage method='add' sync='local'/>
<team>1</team>
<action>1</action>
</event>
</query>
</iq>
JSON object
<iq from='scoutista@zaphod/1' to='marathon-bot@zaphod/server'
id='6155:webclient' xmlns='jabber:client'>
<query xmlns='http://cipriani/game/capture'>
<event marathon='1' timestamp='1255052864229' type='action' id='1255052864229'>
<storage method='add' sync='local'/>
<team>1</team>
<action>1</action>
</event>
</query>
</iq>
SQL
API
<iq type='set' from='marathon-bot@zaphod'
to='pubsub.zaphod' >
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<publish node='/home/zaphod/marathon-bot/events'>
<item>
<event marathon='1'
timestamp='1255059679512'
type='action'
id='1255059679512'>
<storage method='add' sync='local'/>
<team>2</team><action>1</action>
</event>
</item>
</publish>
</pubsub>
</iq>
Desafios da implementação
Garantir o funcionamento do placar caso a Internet saia do ar.
Garantir o funcionamento caso qualquer nó da arquitetura saia do ar (servidor xmpp, marathon-bot, apache).
Evitar perda de dados caso falte energia.
Com a recuperação de algum dos problemas acima, publisher, servidores e subscribers devem ser sincronizados automaticamente.
Sincronização do publisher deve evitar grandes volumes de dados.
Sincronização conjunta de todos os subscribers não deve derrubar o servidor.
Em sistemas de tempo real, muitas vezes o cache não se aplica.
Garantir que o valor do placar seja o mesmo para publishers e subscribers.
Remoção de eventos.
Avisar os subscribers caso ocorra qualquer problema para que ele não ache que o jogo parou.
Garantir que nenhum evento vai ser salvo duas vezes ou entregue duas vezes aos subscribers.