Introduzione
Tramite WebSockets ed EventSource, HTML5 consente agli sviluppatori di creare app web che comunicano con un server in tempo reale. Stream Congress (disponibile nel Chrome Web Store) fornisce aggiornamenti in tempo reale sul funzionamento del Congresso degli Stati Uniti. Trasmette aggiornamenti di piano sia dalla Camera che dal Senato, aggiornamenti di notizie pertinenti, tweet dei membri del Congresso e altri aggiornamenti sui social media. L'app deve essere lasciata aperta tutto il giorno, in quanto rispecchia gli affari del Congresso.
Iniziare con WebSocket
Le specifiche WebSockets hanno ottenuto un po' di attenzione in merito a ciò che consente: un socket TCP stabile e bidirezionale tra il browser e il server. Non esiste un formato dati imposto al socket TCP; lo sviluppatore è libero di definire un protocollo di messaggistica. In pratica, la soluzione più comoda è trasferire oggetti JSON come stringhe. Il codice JavaScript lato client per ascoltare gli aggiornamenti in tempo reale è semplice e chiaro:
var liveSocket = new WebSocket("ws://streamcongress.com:8080/live");
liveSocket.onmessage = function (payload) {
addToStream(JSON.parse(payload.data).reverse());
};
Il supporto dei browser per WebSocket è semplice, ma il supporto lato server è ancora in fase formativa. Socket.IO su Node.js fornisce una delle soluzioni lato server più mature e solide. Un server basato su eventi come Node.js è perfetto per WebSocket. Per implementazioni alternative, gli sviluppatori Python possono utilizzare Twisted e Tornado, mentre gli sviluppatori Ruby possono utilizzare EventMachine.
Scopri i crampi
Cramp è un framework web Ruby asincrono che viene eseguito su EventMachine. Scritto da Pratik Naik, membro del team principale di Ruby on Rails. Grazie a un linguaggio specifico per il dominio (DSL) per le app web in tempo reale, Cramp è la scelta ideale per gli sviluppatori web Ruby. Coloro che hanno familiarità con la scrittura di controller in Ruby on Rails riconosceranno lo stile dei Cramp:
require "rubygems"
require "bundler"
Bundler.require
require 'cramp'
require 'http_router'
require 'active_support/json'
require 'thin'
Cramp::Websocket.backend = :thin
class LiveSocket < Cramp::Websocket
periodic_timer :check_activities, :every => 15
def check_activities
@latest_activity ||= nil
new_activities = find_activities_since(@latest_activity)
@latest_activity = new_activities.first unless new_activities.empty?
render new_activities.to_json
end
end
routes = HttpRouter.new do
add('/live').to(LiveSocket)
end
run routes
Poiché Cramp si basa su EventMachine non bloccabile, occorre tenere presente diversi fattori:
- È necessario utilizzare driver di database che non blocchino, come MySQLPlus ed em-mongo.
- È necessario utilizzare server web basati su eventi. Il supporto è integrato per Sottili e Rainbows.
- L'app Cramp deve essere eseguita separatamente dall'app Rails principale alla base dello Stream Congress, nonché riavviata e monitorata in modo indipendente.
Limitazioni attuali
WebSocket ha subito una battuta d'arresto l'8 dicembre 2010, quando è stata pubblicizzata una vulnerabilità di sicurezza. Sia Firefox che Opera hanno rimosso il supporto dei browser WebSocket. Sebbene non esistano polyfill JavaScript puri, esiste un elemento di riserva Flash che è stato ampiamente adottato. Tuttavia, affidarsi a Flash è tutt'altro che ideale. Anche se Chrome e Safari continuano a supportare i WebSocket, è emerso chiaramente che, per supportare tutti i browser moderni senza fare affidamento su Flash, i WebSocket avrebbero dovuto essere sostituiti.
Rollback al polling AJAX
Abbiamo preso la decisione di abbandonare WebSocket e di tornare ai sondaggi AJAX "vecchio scuola". Sebbene molto meno efficiente dal punto di vista dell'I/O su disco e rete, il polling AJAX ha semplificato l'implementazione tecnica di Stream Congress. Ma soprattutto, la necessità di un'app Cramp separata è stata eliminata. L'endpoint AJAX è stato invece fornito dall'app Rails. Il codice lato client è stato modificato per supportare il polling AJAX di jQuery:
var fillStream = function(mostRecentActivity) {
$.getJSON(requestURL, function(data) {
addToStream(data.reverse());
setTimeout(function() {
fillStream(recentActivities.last());
}, 15000);
});
};
AJAX polling, though, is not without its downsides. Relying on the HTTP request/response cycle means that the server sees constant load even when there aren't any new updates. And of course, AJAX polling doesn't take advantage of what HTML5 has to offer.
## EventSource: The right tool for the job
Up to this point, a key factor was ignored about the nature of Stream Congress: the app only needs to stream updates one way, from server to client - downstream. It didn't need to be real-time, upstream client-to-server communication.
In this sense, WebSockets is overkill for Stream Congress. Server-to-client communication is so common that it's been given a general term: push. In fact, many existing solutions for WebSockets, from the hosted [PusherApp](http://pusherapp.com) to the Rails library [Socky](https://github.com/socky), optimize for push and don't support client-to-server communication at all.
Enter EventSource, also called Server-Sent Events. The specification compares favorably to WebSockets in the context to server to client push:
- A similar, simple JavaScript API on the browser side.
- The open connection is HTTP-based, not dropping to the low level of TCP.
- Automatic reconnection when the connection is closed.
### Going Back to Cramp
In recent months, Cramp has added support for EventSource. The code is very similar to the WebSockets implementation:
```ruby
class LiveEvents < Cramp::Action
self.transport = :sse
periodic_timer :latest, :every => 15
def latest
@latest_activity ||= nil
new_activities = find_activities_since(@latest_activity)
@latest_activity = new_activities.first unless new_activities.empty?
render new_activities.to_json
end
end
routes = HttpRouter.new do
add('/').to(LiveEvents)
end
run routes
Un problema significativo da tenere presente con EventSource è che le connessioni interdominio non sono consentite. Ciò significa che l'app Cramp deve essere gestita dallo stesso dominio streamcongress.com dell'app Rails principale. A questo scopo, puoi utilizzare il proxy sul server web. Supponendo che l'app Cramp sia alimentata da Thin e in esecuzione sulla porta 8000, la configurazione Apache appare così:
LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so
LoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so
LoadModule proxy_balancer_module /usr/lib/apache2/modules/mod_proxy_balancer.so
<VirtualHost *:80>
ServerName streamcongress.com
DocumentRoot /projects/streamcongress/www/current/public
RailsEnv production
RackEnv production
<Directory /projects/streamcongress/www/current/public>
Order allow,deny
Allow from all
Options -MultiViews
</Directory>
<Proxy balancer://thin>
BalancerMember http://localhost:8000
</Proxy>
ProxyPass /live balancer://thin/
ProxyPassReverse /live balancer://thin/
ProxyPreserveHost on
</VirtualHost>
Questa configurazione imposta un endpoint EventSource su streamcongress.com/live
.
Polyfill stabile
Uno dei vantaggi più significativi di EventSource rispetto a WebSocket è che l'elemento di riserva è completamente basato su JavaScript, senza alcuna dipendenza da Flash. Il polyfill di Remy Sharp a questo scopo prevede l'implementazione del lungo sondaggio nei browser che non supportano EventSource in modo nativo. Pertanto, EventSource funziona attualmente su tutti i browser moderni con JavaScript attivato.
Conclusione
HTML5 apre le porte a molte nuove ed entusiasmanti possibilità di sviluppo web. Con WebSocket ed EventSource, gli sviluppatori web ora dispongono di standard puliti e ben definiti per abilitare le app web in tempo reale. Tuttavia, non tutti gli utenti utilizzano browser moderni. Quando si sceglie di implementare queste tecnologie, è necessario considerare il degrado graduale. Inoltre, l'uso di strumenti lato server per WebSocket ed EventSource è ancora nelle fasi iniziali. È importante tenere a mente questi fattori durante lo sviluppo di app HTML5 in tempo reale.