Введение
Благодаря WebSockets и EventSource HTML5 позволяет разработчикам создавать веб-приложения, которые взаимодействуют с сервером в режиме реального времени. Stream Congress (доступен в Интернет-магазине Chrome ) предоставляет актуальные обновления о работе Конгресса США. Он транслирует обновления как Палаты представителей, так и Сената, актуальные новости, твиты членов Конгресса и другие обновления социальных сетей. Предполагается, что приложение будет оставаться открытым весь день, поскольку оно фиксирует дела Конгресса.
Начинаем с вебсокетов
Спецификация WebSockets привлекла немало внимания благодаря тому, что она обеспечивает: стабильный двунаправленный TCP-сокет между браузером и сервером. Для сокета TCP не существует какого-либо формата данных; разработчик может определить протокол обмена сообщениями. На практике передача объектов JSON в виде строк наиболее удобна. Клиентский код JavaScript для прослушивания текущих обновлений понятен и прост:
var liveSocket = new WebSocket("ws://streamcongress.com:8080/live");
liveSocket.onmessage = function (payload) {
addToStream(JSON.parse(payload.data).reverse());
};
Хотя поддержка WebSockets браузерами проста, поддержка на стороне сервера все еще находится на стадии формирования. Socket.IO на Node.js предоставляет одно из наиболее зрелых и надежных серверных решений. Сервер, управляемый событиями, такой как Node.js, идеально подходит для WebSockets. Для альтернативных реализаций разработчики Python могут использовать Twisted и Tornado , а разработчики Ruby — EventMachine .
Представляем судорогу
Cramp — это асинхронная веб-инфраструктура Ruby, работающая поверх EventMachine. Его написал Пратик Найк , член основной команды Ruby on Rails. Cramp, предоставляющий доменно-ориентированный язык (DSL) для веб-приложений реального времени, является идеальным выбором для веб-разработчиков Ruby. Те, кто знаком с написанием контроллеров на Ruby on Rails, узнают стиль Крэмпа:
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
Поскольку Cramp располагается поверх неблокирующего EventMachine, следует учитывать несколько моментов:
- Необходимо использовать неблокирующие драйверы базы данных, такие как MySQLPlus и em-mongo .
- Необходимо использовать веб-серверы, управляемые событиями. Встроена поддержка Thin и Rainbows .
- Приложение Cramp необходимо запускать отдельно от основного приложения Rails, которое обеспечивает работу Stream Congress, перезапускать и контролировать независимо.
Текущие ограничения
WebSockets потерпел неудачу 8 декабря 2010 года, когда была опубликована информация об уязвимости безопасности. И Firefox, и Opera удалили поддержку WebSockets в браузерах. Хотя полифилов на чистом JavaScript не существует, существует запасной вариант Flash , который получил широкое распространение. Однако полагаться на Flash далеко не идеально. Несмотря на то, что Chrome и Safari продолжают поддерживать WebSockets, стало ясно, что для поддержки всех современных браузеров без использования Flash необходимо заменить WebSockets.
Откат к опросу AJAX
Было принято решение отказаться от WebSockets и вернуться к опросам AJAX «старой школы». Хотя опрос AJAX гораздо менее эффективен с точки зрения дискового и сетевого ввода-вывода, он упростил техническую реализацию Stream Congress. Самое главное, что отпала необходимость в отдельном приложении Cramp. Вместо этого конечная точка AJAX была предоставлена приложением Rails. Код на стороне клиента был изменен для поддержки опроса jQuery AJAX:
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
Важная проблема, которую следует учитывать при использовании EventSource, заключается в том, что междоменные соединения не допускаются. Это означает, что приложение Cramp должно обслуживаться из того же доменаstreamcongress.com, что и основное приложение Rails. Это можно сделать с помощью проксирования на веб-сервере. Если предположить, что приложение Cramp работает на Thin и работает на порту 8000, конфигурация Apache выглядит следующим образом:
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>
Эта конфигурация устанавливает конечную точку EventSource по streamcongress.com/live
.
Стабильный полифилл
Одним из наиболее значительных преимуществ EventSource перед WebSockets является то, что резервное копирование полностью основано на JavaScript и не зависит от Flash. Полифил Реми Шарпа достигает этого, реализуя длинный опрос в браузерах, которые не поддерживают EventSource изначально. Таким образом, EventSource сегодня работает во всех современных браузерах с включенным JavaScript.
Заключение
HTML5 открывает двери для многих новых и интересных возможностей веб-разработки. Благодаря WebSockets и EventSource веб-разработчики теперь имеют чистые, четко определенные стандарты, позволяющие создавать веб-приложения в реальном времени. Но не все пользователи используют современные браузеры. При выборе внедрения этих технологий необходимо учитывать плавную деградацию. А инструменты на стороне сервера для WebSockets и EventSource все еще находятся на ранних стадиях. Важно учитывать эти факторы при разработке приложений HTML5, работающих в режиме реального времени.