Синхронизация открытых вкладок

в 12:52, , рубрики: client side optimization, javascript, Веб-разработка, Клиентская оптимизация, метки: ,

На основе Cookies, для сайтов с общим наддоменом

Хочу поделиться, как это удалось осуществить для одного проекта. Сложность использования других известных методов, заключалась в том, что проект не был привязан к единому доменному имени, а был локализован на сетке из поддоменов. То есть сайты проекта располагались на доменах третьего уровня. Данное обстоятельство вызывало некоторые неудобства из-за Same Origin Police.

Что за синхронизация и для чего она нужна?

Разработчикам, которые ещё не сталкивались с подобными проблемами, пожалуй, будет интересно. Представьте себе веб-сервис (к примеру, социальную сеть) который должен в режиме online информировать пользователя о разного рода «новых сообщениях», «новых друзьях» и тому подобное. Веб-сервис, отслеживающий online индекс, какого-нибудь, Доу Джонса, или не дай бог, самой ММВБ.

Со страниц таких проектов, постоянно, с некоей периодичностью, отправляются запросы к серверу для получения актуальной информации. Более того, некоторые сервисы устанавливают постоянное соединение с клиентом, чтобы держать этого клиента в курсе своих тёмных серверных делишек в режиме реального времени.

Больное место подобных систем начинается, как это ни странно — с браузеров. С браузеров, которые поддерживают открытие страниц «в новой вкладке». И ещё больнее, когда в проекте, для различного рода информации, имеется отдельный тип страниц. (но это уже совсем другая история — история дизайна).

На самом деле, пользователю действительно неудобно постоянно кликать по ссылкам с текущей страницы, чтобы посмотреть «а ну ка, какую там оценку мне поставили».
И пользователь открывает сначала одну вкладку, для навигации, затем вторую вкладку, для отслеживания оценок, затем третью — для отслеживания новых сообщений. Количество одновременно открытых вкладок находится в прямой зависимости от величины желания пользователя «контролировать всё».

Сколько серверов нужно иметь чтобы быстро обработать запрос от каждого клиента с каждой такой открытой страницы? Тем более, если запросы идут асинхронные и с некоей периодичностью.

С необходимостью решения подобных проблем сталкиваются как растущие проекты, так и динозавры поглотившие web.

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

Почему Cookies?

Набирающий популярность Local Storage (и его модификации) не подходил по той простой причине, что на него также накладывалось правило ограничения домена (same origin policy). К тому же, необходимо было отслеживать состояние активных (открытых) вкладок, что требует дополнительной доли ресурсов. И, ко всему прочему, функционирует он, пока что, не во всех браузерах. По тем же причинам, на Flash также не было особой надежды, как и на другие вкусные фичи. Неплохой алгоритм, кстати, был описан вот тут.

В общем, после многодневного битья головой об стенку и обгрызывания краешков офисных столов, вспомнился древний и нативный браузерный механизм — Cookies.

Схема работы

  1. Загрузка страницы
    Определяется ID текущей страницы, и устанавливается собственная кука (отмечаемся).
    Устанавливается интервал, во время срабатывания которого, проверяется возможность выполнения запроса к серверу.
  2. Срабатывание интервала
    Получаем минимальный ID открытой страницы (из кук).
    Если минимальным ID является собственный ID, то только в этом случае производится запрос к серверу для получения данных.
    Иначе мы проверяем готовность данных, и забираем их, если требуется.
    Не забываем обновить самоидентификатор.
  3. Запрос к серверу
    Здесь ничего особенного не происходит, за исключением того, что серверный ответ обычных данных не содержит, а лишь устанавливает куку с результатом. В моём случае этого было достаточно.
    Обходим все установленные для вкладок куки, и определяем их значение как «ready».
  4. Получение данных
    В том случае, когда минимальный ID не совпадает с собственным ID, проверяется значение собственной куки.
    Если её значение «ready» — это означает, что данные благополучно получены и требуется их забрать.
    Забираем данные и обрабатываем их в контексте текущей страницы.

Особенности

  • У кук есть превосходное свойство — время жизни, которое не нужно отслеживать — за этим следит браузер.
  • При открытии вкладки, вкладка «отмечается», устанавливая куку со своим идентификатором, время жизни которой ограничено.
  • Куки устанавливаются с продолжительностью жизни чуть более интервала самообновления.
  • Серверный ответ также устанавливается в куку. В моём случае это небольшой объём данных, который служит лишь указанием к действию.
  • Если вкладка не является главной, то во время срабатывания интервала, вкладка только забирает данные из куки-хранилища, и выполняет их обработку.
  • Закрытая вкладка перестаёт поддерживать свою куку и кука благополучно умирает. Управление перехватывает раньше всех открытая вкладка.
  • Кука с данными, уничтожается перед запросом к серверу. К этому времени, все остальные вкладки успевают её обработать.
  • Чтобы обеспечить сохранность собственной куки, главная вкладка при запросе к серверу переустанавливает куку с собственным ID.

Сейчас, после множества экспериментов, думаю, что есть место, где разгуляться фантазии для оптимизации данного механизма.

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

Синхронизация состояния мессенджера осуществлялась с помощью Local Storage. В случае его отсутствия, ничего не оставалось делать, как производить запрос к серверу для получения последнего состояния.

Линчевание

Начну с недостатков:

  • Главный недостаток это то, что весь багаж кук отправляется с каждым запросом на сервер.
  • Второй, не менее важный недостаток — это ограничение объема данных в серверном ответе. Как известно, максимальный объём одной куки ~4 kB (в древних IE — это общий объём всех кук для одного домена). То есть «наше общение» ограничивается указанием действий и передачей необходимых параметров.
  • Данный механизм действителен в пределах только одного браузера.
  • Метод ещё недостаточно отлажен. Код постоянно обновляется, так как выявляется множество мелочей, как отрицательных, так и положительных, но требующих оптимизации.

Достоинства:

  • Кроссплатформенность. Пожалуй, это довольно существенное положительное обстоятельство. Механизм кук поддерживается всеми браузерами без исключений.
  • Забота о слежении «включенности/отключенности» вкладок возложена на нативную функцию браузера. Это избавило от проблем с beforeUnload функциями, некорректно работающими в некоторых браузерах, вкупе с другими, не до конца стандартизированными возможностями.
  • Минимум сложности. Метод не требует никаких SharedObject'ов и прочих WebWorkers'ов. А также их надстроек, делающих использование данных фич кроссбраузерными.

Для более детального ознакомления с описанным методом, можно скачать архивную копию.

PS: В архиве черновая версия.

Автор: istem

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js