Постоянное соединение между браузером и сервером
Сегодня мы рассмотрим основные варианты реализации этого принципа и как он сочетается с обсуждавшимися в предыдущих статьях серии темами.
Транспорт
Так как одной из сторон постоянного соединения является браузер, вопрос кроссбраузерности при его реализации стоит не менее остро, чем, например, при верстке. В 2001 году, когда появился на свет самый часто вспоминаемый недобрым словом браузер в мире, о подобных технологиях постоянного соединения между браузером и сервером практически никто не задумывался даже отдаленно.Существуют несколько протоколов и связанных с ними технологий, которые позволяют реализовать постоянное с точки зрения приложения соединение между браузером и сервером, обычно их называют транспортами. Каждый из них обладает разной производительностью, особенностями реализации и нагрузкой на серверную часть. Возможно не полный их список c краткими пояснениями:
- WebSocket: пожалуй, самый эффективный с точки зрения производительности и нагрузки на сервер транспорт. Протокол относительно новый, появился в рамках работы над HTML5. Доступен только в очень свежих браузерах, имеет несколько более-менее стандартных версий. Используется одно соединение для обоих направлений обмена сообщениями.
- EventSource: появился примерно в то же время, что и WebSocket, но по задумке должен использоваться для получения односторонних уведомлений от сервера. В совокупности с простыми AJAX запросами для отправки событий из браузера может использоваться для двустороннего общения. Но так как он доступен примерно в тех же версиях браузеров, что и WebSocket, со сценариями, когда он оказывался бы более предпочтительным, я не сталкивался. Технически очень похож на следующий транспорт.
- AJAX Multipart aka HTTP Streaming: после получения HTTP-запроса от клиента сервер не «отпускает» его и по мере поступления отправляет в него свои сообщения. Для отправки сообщений из браузера при необходимости создается второе соединение.
- AJAX/HTTP Polling: в отличии от предыдущего транспорта, сервер закрывает HTTP-соединение после каждого отправленного в него сообщения или по прошествии определенного таймаута (обычно порядка 20-40 секунд). А браузер сразу же после получения сообщения открывает новое соединение, таким образом у сервера по-прежнему практически всегда есть соединение, куда можно отправить сообщения. Хоть по нагрузке на сервер этот вариант самый тяжелый, поддерживают его практически все браузеры.
- Adobe Flash: эта платформа может эмулировать поддержку WebSocket при определенном стечении обстоятельств (удачная комбинация Flash-плеера и браузера). Немного нетривиальна в настройке из-за своих особенностей.
Все вышеизложенные транспорты в конечном итоге основываются на протоколе HTTP. Большинство из современных браузеров ограничивают количество одновременных HTTP-соединений с доменом до двух, что как раз достаточно даже для менее эффективных вариантов.
В любом случае работать напрямую с транспортами не обязательно, благо существует большое количество библиотек и сервисов, позволяющих от них абстрагироваться, к ним и переходим.
Абстракция
По сути такие библиотеки состоят из двух частей: клиентской на JavaScript и серверной для одной или нескольких платформ. Клиент определяет какой из доступных в текущем браузере транспортов является наиболее эффективным и с его помощью устанавливает соединение с сервером, который поддерживает несколько протоколов. С точки зрения разработчика интерфейс, ими предоставляемый, не зависит от транспорта и примерно одинаков:- Метод для отправки сообщения противоположной стороне.
- Регистрация обработчика события, который будет вызван при получении сообщения от противоположной стороны, с содержанием сообщения в аргументе.
- Метод, который будет вызван при установке и разрывании соединения.
- Инициатором соединения по очевидным причинам всегда является клиент, так что у него есть дополнительный механизм для этого, с возможностью указать какие-то настройки.
- Node.js на JavaScript
- На Erlang есть несколько очень эффективных HTTP-серверов:
- Tornado на Python
- netty на Java
- socket.io: поддерживает практически все возможные транспорты, включая Flash. Основная серверная платформа — node.js, силами сторонних разработчиков есть реализации протокола на других платформах. Имеет спорную репутацию, проект довольно громоздкий, в некоторых ситуациях ведет себя непредсказуемо.
- SockJS: очень молодой проект, поддерживает необходимый минимум транспортов, прост в эксплуатации. Относительно стабилен и предсказуем. Серверная часть доступна на node.js, Tornado и cowboy/misultin, активно работают над другими платформами.
Вернемся к интерактивным сайтам
Надеюсь, только что закончившегося лирического отступления на ¾ статьи Вам будет достаточно, чтобы составить общее представление о построении постоянного соединения между браузером и сервером, а желательно и определиться с каким-то решением для автоматического выбора наиболее эффективного транспорта в контексте именно Вашего проекта.Получив примитивный интерфейс в виде «отправить сообщение / отреагировать на сообщение» необходимо определиться с тем, что же мы будем передавать в этих сообщениях и как будем на них реагировать.
С форматом сериализации сообщений все довольно просто: выбор между XML и JSON очевиден в пользу последнего, а заморачиваться с чем-то более экзотическим смысла мало (хотя давно хочу попробовать в этой роли Protocol Buffers или BSON, но никак руки не доходят).
Намного интереснее вопрос о том, что, собственно, будет в этих сообщениях содержаться. В предыдущей статье мы остановились на использовании фреймворка для организации кода JavaScript-клиента. Предлагаемая ими концепция модели обычно по-умолчанию предоставляет возможность синхронизации с сервером посредством AJAX запросов и механизм изменения этого поведения. Для использовавшегося в качестве примера Backbone.js для этого необходимо переопределить функцию Backbone.sync. При сохранении модели клиент будет отправлять объект с идентификатором модели и списком её изменений. Запрос изменений с сервера будет происходить асинхронно, то есть после отправки сообщения о том, что нужны данные для такой-то модели, посредством метода fetch он сам не получит ответа. Собственно изменения в модели произведет обработчик получения сообщений, в котором должна быть реализована соответствующая логика. Далее подписанные на события изменений в моделях объекты-представления будут соответствующим образом обновлять DOM-дерево страницы, отображая пользователю нужную информацию. Это, пожалуй, наиболее правильный способ интегрировать постоянное соединение и клиентский фреймфорк.
Основными минусами его является очень серьезный объем работы по разработке клиентской части, а также дублирование достаточно большой части логики и HTML-шаблонов между серверной и клиентской сторонами. Я бы рекомендовал использовать этот подход, только если позволяют трудовые ресурсы (читай: есть хотя бы отдельный специализирующийся на JavaScript разработчик), либо когда проект по каким-то причинам решил отказаться от реализации статичного HTML-интерфейса.
В следующей статье я расскажу о менее трудозатратном способе добиться того же результата, который основан на жертве идеологической правильностью в пользу минимизации повторного написания кода.
Эта статья — третья в серии про Интерактивные сайты, автор — Иван Блинков, основано на личном опыте, внешние источники информации при написании не использовались.
До встречи на страницах Insight IT!
До встречи на страницах Insight IT!
Комментариев нет:
Отправить комментарий