Содержание
В последние годы WebSocket у всех на слуху. И неспроста. Все мы имели удовольствие наблюдать подъем веб-приложений, работающих в реальном времени. Взять хотя бы онлайн чаты с онлайн-статусами пользователей, уведомлениями или магически появляющимся на странице контентом. В этой статье мы расскажем о трудностях, которые сумел преодолеть web и о том, насколько легко с помощью Rails и ActionCable реализуются функции, работающие в реальном времени.
Немного истории
Как известно, HTTP (Hypertext transfer protocol) — это прикладной протокол передачи гипермедиа, классический пример модели клиент-серверного взаимодействия. Изначально (в 1991 году) он разрабатывался для передачи гипертекстовых документов. То есть только HTML документов, соединенных гиперссылками и передаваемых по “однострочному” протоколу HTML 0.9.
В последствии, начиная с версии HTTP 1.0, вышедшей в 1996 году, возможности протокола существенно расширились. HTTP стал гипермедийным и начал использоваться для других целей, таких как передача CSS, JS документов и мультимедиа. Сегодня это очевидно, но 20 лет назад казалось прорывом.
Впрочем, HTTP 1.0 имел целый ряд серьезных недостатков. Прежде всего отмечалось нерациональное использование сетевой инфраструктуры. HTTP 1.0 открывал, а затем закрывал TCP соединение для каждого полученного URL. В результате небольшие по объему HTTP запросы демонстрировали очень низкую производительность из-за медленного старта TCP (хэндшейк). К слову, подобная проблема преследует XHR и по сей день, так как для каждого XHR запроса устанавливается новое HTTP соединение.
Буквально через полгода после появления HTTP 1.0, в 1997 году, вышла следующая версия — HTTP 1.1. А к началу нулевых была утверждена конечная спецификация HTTP 1.1. Новая версия устранила большую часть недостатков, описав cookies, директивы кэширования, поддерживаемое по умолчанию соединение и многое другое.
Why not XHR polling?
Как вы уже поняли, XHR особой производительностью не отличается. Но с этим еще можно смириться. Другое дело — размер HTTP заголовка, в современных приложениях с директивами кэширования, cookies и самими данными он не должен превышать двух килобайт. Ведь для обработки больших потоков нужны серьезные процессорные мощности. Нельзя забывать и о том, что веб-приложение будет принимать обычные запросы.
В случае с долгим запросом (long polling) все становится еще печальнее. Удерживаемое сервером соединение будет использовать ресурсы CPU.
И все же ставить крест на XHR не стоит. У этого подхода есть свои цели. Он позволяет вам в полной мере использовать положительные стороны HTTP, такие как cookies и кэширование. А это значит, что пока не появятся WebSocket REST API, мы будем использовать XHR.
Shiny WebSockets!
WebSocket — прикладной протокол двустороннего обмена данными между клиентом и сервером. Это самостоятельный протокол, использующий, как TCP транспорт, так и HTTP (для установки соединения с помощью заголовка Upgrade). Стандарт WebSocket и WebSocket API был утвержден в 2011 году.
Перед вами наверное самый универсальный и гибкий транспорт, который доступен в браузере. При всей своей низкоуровневой сложности он предоставляет довольно простой программный интерфейс, позволяющий разработчику организовать двустороннее взаимодействие без лишних усилий. В тоже время, ему чужды проблемы XHR. Ведь соединение устанавливается лишь раз, стало быть нагрузка на сервер и сеть (bandwidth allocation) значительно меньше. В результате мы получаем неплохой прирост к общей производительности.
Rails и WebSocket. ActionCable
До недавних пор Rails вполне себе обходилась XHR полингом, о недостатках которого мы говорили выше. Когда же возникала необходимость добавить в RoR приложение поддержку WebSocket, обычно приходилось попотеть. В случае с Faye он конечно делал то, что нужно, но хранил данные о соединениях в памяти, что связывало руки, когда речь шла о масштабировании.
Однако подъем real time веба и таких фреймворков как Phoenix, у Rails просто не осталось выбора. Для того чтобы оставаться на плаву, пришлось адаптироваться. Так появился ActionCable.
ActionCable — это новый фреймворк, вышедший вместе с Rails 5. В более поздних версиях он уже стал частью Rails. Оборачивая все низкоуровневые части WebSocket взаимодействия, он предоставляет нам простой инструмент. ActionCable включает в себя прозрачный API для реализации серверной части, а также тривиальный в использовании JS API.
Также стоит упомянуть, что ActionCable не готов к использованию в высоконагруженных системах, таких как, например, онлайн игры или чаты (когда один канал слушает несколько тысяч пользователей). Причиной этому является сам MRI Ruby. Также многое зависит от адаптера подписки (subscription adapter). В качестве альтернативы можно обратить внимание на AnyCable или Phoenix.
ActionCable on Heroku
Heroku — отличный инструмент и тем не менее, когда возникает необходимость в микросервисах, появляются сложности. А именно — необходимость в очередном Heroku приложении, так как весь трафик, проходящий через Heroku, будет проксироваться на единственную web дину (web dyno).
В случае с WebSocket, ActionCable решает эту проблему за нас, с помощью Rack Hijacking API. Этот функционал позволяет приложениям перехватывать контроль над клиентским сокетом, реализуя вещи, подобные WebSocket, стримингу (ActionController::Live) и пр.
Еще одной сложностью масштабирования приложений является проблема распределенного состояния. В случае с Faye, как уже упоминалось выше, трудности вызывает реализация механизма хранения подписчиков в памяти. Однако с ActionCable такой проблемы не возникнет. Для хранения состояния подписчиков можно использовать любой внешний pub/sub адаптер — postgresql pub/sub, redis pub/sub и т.д (все, кроме inline). И к какому именно серверу будет подключен клиент, просто не имеет значения.
Вывод
Как вы поняли, real time веб-приложения на сегодня — наше все:) И если вы хотите добавить интерактивности: нотификации об изменении состояния, обновление контента в зависимости от каких-либо событий и прочее, можно смело использовать ActionCable. Мы рекомендуем! ActionCable — это масштабируемый, простой в настройке и использовании WebSocket сервер и WebSocket API клиент из коробки. Он работает и работает хорошо.