Мгновенные сообщения

Материал из Eludia
Перейти к: навигация, поиск

Содержание

Время от времени возникает необходимость реализовать в WEB-приложении пересылку пользователям каких-либо сообщений в реальном времени. Это могут быть:

  • сообщения других пользователей (по аналогии с ICQ и прочими P2P IM системами);
  • извещения о некоторых событиях (у вас на подписи...);
  • вообще не текст, но произвольный js-код, например, открывающий определённую страницу в новом окне.

Как это может работать

В любом случае речь идёт о пересылке данных клиенту по инициативе сервера. Как известно, протокол HTTP таких возможностей не поддерживает в принципе: соединения устанавливаются только по инициативе клиентов и нет никакого способа гарантированно поддерживать их в активном состоянии.

Однако можно инициировать периодический процесс опроса клиентом своего "ящика с сообщениями", по аналогии с e-mail-клиентами, которые также используют чисто пассивные протоколы. Ближайший же аналог для WEB — HTTP polling в jabber.

Оптимизационная проблема

Допустим, клиент опрашивает сервер раз в минуту. Это значит, что сообщения будут приходить со средней задержкой в 30 с. В некоторых приложениях это допустимо, в иных — нет. Чтобы гарантировать задержку не более 1 с, необходимо будет опрашивать ящик каждую секунду (это если считать сетевые задержки нулевыми). Что, очевидно, даёт минимальный поток из N запросов в секунду для N пользователей, у которых просто открыт браузер. Точнее даже, не самих пользователей, а браузерных окон (которых вполне может быть и больше).

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

Компромиссное решение

В Eludia.pm горячие сообщения реализованы как статика. Каждый пользователь с номером $id_user раз в $_REQUEST {__im_delay} мс запрашивает файл docroot/i/_mbox/$id_user.txt. Если непрочитанных сообщений нет (то есть практически всегда), он пуст. JS-функция на клиенте получает пустое содержимое и снова ждёт установленный интервал времени.

Когда же сообщение добавляется на сервере (функцией js_im), то происходит следующее:

  • в директории docroot/i/_mbox/by_user появляется соответствующий JSON-файл;
  • в $id_user.txt записывается имя этого файла;
  • остальные файлы в директории проверяются на актуальность: устаревшие и конфликтующие с новым сообщением — удаляются.

Получив в качестве содержимого $id_user.txt, клиент делает второй запрос: на этот раз, чтобы получить само сообщение. Этот запрос уже не статический: действие read для типа _mbox. В результате на клиент отправляется содержимое сообщения, а JSON-файл стирается. Такой подход позволяет обеспечить:

  • проверку прав доступа (сообщение может быть доступно только адресату — если только docroot/i/_mbox/by_user закрыта для всех);
  • гарантированно одноразовую доставку сообщения. Даже если запрос послали 2 клиента с одной сессией, то сообщение получит только 1 из них: тот, в рамках чьего запроса был удалён JSON-файл.

Получив и обработав сообщение, клиент тут же вновь проверяет свой ящик; установленный период выжидается только после пустых ответов. Таким образом, и при больших периодах опроса (минуты) пакеты одновременно созданных сообщений приходят без внутренних задержек.

Фильтрация сообщений

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

Формируя сообщение функцией js_im, можно указывать различные опции. Одна из них — expires — имеет очевидный смысл: время (в секундах относительно 01.01.1970), после которого сообщение должно быть удалено. В качестве значения можно указать массив [ год, месяц, день, час, минута, секунда ] — timestamp будет рассчитан автоматически.

Другая аналогичная опция: session. Если она задана, то сообщение привязывается к сессии и удаляется после повторного входа пользователя в систему.

И, наконец, tag, описывает тему, в рамках которой должно выживать только одно, последнее, сообщение.

Задание периода опроса

Как уже показано, интервал времени для опроса ящика (в миллисекундах) устанавливается путём задания переменной $_REQUEST {__im_delay}. Это значение используется в конце обработки запроса и может быть установлено в процедурах (упомянуты в порядке вызова):

  • select_subset;
  • select_menu;
  • get_page;
  • select_$_REQUEST{type} / get_item_of_$_REQUEST{type};
  • draw_$_REQUEST{type} / draw_item_of_$_REQUEST{type}.

Значение $_REQUEST {__im_delay} копируется в js-переменную window.__im_delay. Таким образом, его можно менять для целевого браузерного окна средствами javaScript-процедур, в том числе и присылаемых в составе сообщений.

Предупреждение

Это должно быть достаточно очевидно, однако отметим ещё раз: для мгновенных сообщений необходимо оптимизировать статическую часть приложения. В частности, использование Apache 1.3x / mod_perl 1.x при опросе раз в секунду для десятков пользователей совершенно неприемлемо.

Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
Разработчику
Администратору
Инструменты