Pull to refresh
VK
Building the Internet

Mail.Ru Агент в браузере: подходы к синхронизации состояния

Reading time5 min
Views15K
Одной из наиболее заметных тенденций современного интернета является ослабление роли «классических» десктопных мессенджеров, еще недавно безраздельно властвовавших на компьютерах пользователей, и возрастание роли мобильных и веб-клиентов. Последние приобрели особенную популярность благодаря почтовым сервисам нового поколения, а также социальным сетям.

Не осталась в стороне и компания Mail.Ru, в 2008 году запустившая первую версию Mail.Ru Агента для веба (далее – просто Агента).




Однако если пользователь может даже не задумываться о разнице между standalone-приложением и веб-клиентом, то для разработчика разница очевидна. А вот трудности и проблемы, которые приходится решать, сменив философию отдельной программы на взаимодействие с браузером, могут быть менее прозрачными.

Первый «враг» разработчика в этом случае… сам пользователь. Вместо того чтобы сосредоточенно общаться со своими друзьями на странице социальной сети или почты, он щелкает по ссылкам и ходит по страницам. А значит — нужно запоминать полное состояние клиента и восстановить его на следующей загруженной странице.

А как только эта проблема решена, пользователь вспоминает, что его браузер поддерживает вкладки, и принимается открывать в новых вкладках, например, письма. В этом случае мы должны синхронизировать состояние клиента, с которым пользователь взаимодействует в данный момент, со всеми открытыми копиями клиента — так, чтобы в любой вкладке клиент выглядел одинаково.

В общем, перед разработчиком возникает целый комплекс проблем сохранения/изменения и синхронизации состояния клиента.

История развития Агента во многом совпадает с историей преодоления разработчиками перечисленных проблем. В процессе нам встретилось немало ожидаемых и неожиданных сложностей, которые приходилось либо преодолевать, либо обходить. Попытаюсь в общих чертах описать, как это выглядело на практике.

Однако вначале определимся с основными понятиями, которыми мы часто пользуемся в нашей рабочей группе. Для понимания этой статьи достаточно запомнить всего четыре термина: это состояние, событие, роутер и клиент.

Состояние – это набор свойств Агента, определяющих его текущий внешний вид (представление). К таким свойствам относится список открытых диалогов, признаки текущего диалога, наличия непрочитанных сообщений в открытых диалогах, состояние псевдо-окон Агента (свернуты/развернуты), typing notification и т.д.

Состояние может быть изменено событиями, которые содержат информацию о том, что именно в состоянии изменилось. Событие может быть инициировано как сервером (например, когда пользователю кто-то написал сообщение), так и клиентами.

Клиент – это программа, которая выполняется в браузере пользователя, и отображает пользовательский интерфейс в соответствии с состоянием. Кроме того клиент может генерировать события – например, когда пользователь открывает или закрывает диалог, разворачивает или сворачивает псевдо-окно.

Наконец, роутер – это некий «черный ящик», к которому подключаются все клиенты, и который всегда «знает» состояние последнее Агента. Основная задача роутера – принимать события от сервера, а также клиента, с которым в данный момент взаимодействует пользователь, и рассылать их остальным подключенным клиентам. Таким образом, роутер обеспечивает синхронизацию состояния между всеми копиями клиента.

NB: Вообще, с точки зрения сетевой терминологии, правильнее было бы называть наш роутер хабом, но так уж исторически сложилось. :)

I этап. Серверная синхронизация.

Первым решением, которое мы попробовали, была серверная синхронизация, то есть в качестве роутеров выступали «настоящие» выделенные серверы. Преимущества казались очевидными: независимо от того, из какого браузера или компьютера выполняется подключение, состояние всех клиентов оказывается синхронизированным. Также очень соблазнительной была простота дизайна клиента – он получался действительно «тонким», ведь его задача заключалась лишь во взаимодействии с пользователем и в отрисовке изменений состояния.

Однако практически сразу выявилось два узких места:
  • задержки сервера и overwrite состояний;
  • задержки сети.

В итоге Агент более-менее справлялся лишь с чатом в одном-двух диалогах на быстром сетевом соединении и при условии умеренной активности пользователя. Как только пользователь начинал общаться с большим количеством контактов и делать это быстро, синхронизация «разваливалась» под грузом сетевого оверхеда, особенно при большом количестве открытых окон – ведь одни и те же события нужно было доставлять отдельно в каждое окно. Помимо низкой скорости отклика, такой подход создавал высокую нагрузку на роутеры, поэтому довольно быстро мы от него отказались.

II этап. Перенос синхронизации на клиент.

Очевидным ответом на вопросы серверной синхронизации стал перенос этой задачи на клиентский компьютер. Тут и вычислительная мощность, не разделяемая с другими пользователями, и отсутствие длинного «плеча», присущего системе «браузер1 — сервер — браузер2».

В качестве решения был выбран Adobe Flash, а точнее – его класс Flash Local Connection, предназначенный как раз для диспетчеризации событий. В этой архитектуре в качестве роутера выступал первый запущенный экземпляр клиента. Каждая новая копия клиент проверяла, создан ли уже роутер, соединялась с ним и начинала передавать и получать состояния от него, а не напрямую от сервера. По сути получился «тонкий клиент с толстыми элементами». :)

Эта реализация принесла облегчение по сравнению с серверной синхронизацией, однако довольно быстро мы столкнулись с другими сложностями – на этот раз, к сожалению, «закопанными» внутри самого Flash Player'а.

Для начала выяснилось, что внутреннее хранилище Flash Player не позволяет часто записывать и читать данные и хранить их большие объемы, что было характерным сценарием при интенсивном использовании Агента.

Однако самые большие проблемы начались после очередного обновления Flash Player’а, когда по непонятным причинам резко упала скорость работы Local Connection, и сигнал от одной вкладки к другой стал буквально «теряться в проводах». В сочетании с не особенно высокой скоростью работы Flash Player’а в целом это часто приводило к значительной рассинхронизации.

III этап. Клиентская синхронизация средствами HTML 5.

Выход HTML 5 и реализация его поддержки в большинстве современных браузеров дали разработчикам веб-приложений новые инструменты. Лучшей новостью для нас стало появление собственного локального хранилища браузера. Теперь без применения внешних технологий стало возможным хранить локально около 5 Мб данных и предоставлять быстрый доступ к ним каждому экземпляру клиента. По сути это тот же самый Local Connection, только значительно быстрее и надежнее, а так же без ограничений на чтение и запись.

На практике это позволяет нам отказаться от роутера и хранить общее состояние, которое «видят» и легко изменяют все экземпляры клиента (разумеется, это работает только в рамках одного браузера). Однако одновременно с новыми возможностями возникли и новые трудности. Например, для синхронизации состояний между различными доменами, на которых размещены страницы с Агентом, создается iframe, на который действует правило ограничения домена (same origin policy). Чтобы обойти это ограничение пришлось использовать библиотеку EasyXDM и транспорт postMessage. Интересно, что данная библиотека дает возможность кросс-доменного обмена сообщения с верификацией отправителя-получателя.

Сейчас этот метод синхронизации является основным, хотя в старых браузерах, не поддерживающих локальное хранилище, мы по-прежнему вынуждены использовать синхронизацию состояния через Flash Player.

А напоследок я скажу...

Внешний вид Веб-Агента на текущий момент не вполне соответствует нашим представлениям о прекрасном, но мы активно работаем над этим и уже совсем скоро будем готовы официально представить новый дизайн. Ждите анонсов!

Илья Наумов,
руководитель проекта Mail.Ru Агент
Tags:
Hubs:
Total votes 60: ↑41 and ↓19+22
Comments41

Articles

Information

Website
vk.com
Registered
Founded
Employees
5,001–10,000 employees
Location
Россия
Representative
Миша Берггрен