|
|
dkLab | Конструктор | dklab_multiplexor: постоянное JavaScript-соединение с сервером в условиях сотен тысяч онлайн-клиентов
2009-09-12
Хотите помочь с разработкой и улучшением библиотеки? Это можно сделать в социальной сети для OpenSource-разработок GitHub, построенной на основе системы контроля версий Git.
Внимание! Данный инструмент устарел с выходом
его идейного Dklab_multiplexor это инструмент, который позволяет держать одновременно сотни тысяч долгоживущих открытых HTTP-соединений с сервером. Например, если на вашем сайте находится одновременно несколько сот тысяч посетителей, каждый из них может быть связан с сервером постоянным соединением, установленным из JavaScript. Это, например, полезно при организации онлайн-чатов или мгновенных уведомлений.
Зачем это нужно?Предположим, посетители вашего сайта могут посылать друг другу сообщения. Если получатель в момент отправки находится на сайте (просматривает какую-либо страницу), ему немедленно выдается уведомление (всплывающее окошко на JavaScript).
Данную задачу можно решить двумя способами.
Краткая инструкция по применениюЗапустим МультиплексорЗапустите Мультиплексор и оставьте его работать:
Попробуйте telnet-ом установить соединение с портом 8088 (см. WAIT_ADDR). Если telnet "подвиснет", значит, все работает правильно:
Если Мультиплексор не запустился, скорее всего, в вашей системе отсутствует библиотеки libevent и Event::Lib. Установить их в RHEL-системе можно командами:
Прикинемся браузером клиентаПопробуем теперь проэмулировать браузер Клиента с идентификатором
Команда
Итак, в реальной ситуации JavaScript-код Клиента устанавливает HTTP-соединение с Мультиплексором, используя XMLHttpRequest. При выполнении GET-запроса JavaScript указывает идентификатор пользователя, чтобы получать только сообщения для этого пользователя. Клиент ждет либо прихода данных о новых сообщениях от Мультиплексора (тогда он его отображает), либо же завершения соединения через 300 секунд. В обоих случаях Клиент сразу же устанавливает новое соединение с Мультиплексором и ждет от него нового ответа, и так до бесконечности. Таким образом, за счет медленных соединений поддерживается низкая загрузка сервера, а также обеспечивается мгновенность передачи нового сообщения от Сервера к Клиенту. Передадим Мультиплексору данные для КлиентаОткройте вторую консоль на сервере, где у вас "висит" только что запущенный
После этого нажмите
Мультиплексор реализует буферизацию данных. Таким образом, если на момент передачи сообщения некоторый Клиент не был подключен к Мультиплексору (например, он как раз переходит на другую страницу сайта), Мультиплексор сохранит сообщение (максимум на 20 секунд, см. OFFLINE_TIMEOUT) и передаст его Клиенту, как только он подключится. Фактически, мы сейчас "руками" проэмулировали то, что должен делать скрипт на сайте при приходе нового сообщения для пользователя 1z2y3z. Соответствующий PHP-код может выглядеть так:
Ура, заработало!Если вы все сделали правильно, то Команда ONLINE: получение списка online-клиентовЕсли послать Мультиплексору не текст HTTP-ответа для браузера, а строчку "ONLINE\n", то он ответит в соединение списком всех ID клиентов, находящихся в online-режиме. Элементы списка разделяются запятыми, а сам список оканчивается точкой "." на отдельной строке. (Точка нужна, чтобы удостовериться: данные были прочитаны полностью и не оборвались.) Результат может выглядеть, например, так:
Пример PHP-кода:
Подробная инструкция и технические деталиИтак, мультиплексор соединений dklab_multiplexor (далее "Мультиплексор") это демон, позволяющий держать значительное количество открытых ждущих TCP-соединений (несколько десятков тысяч) и передавать через них сообщение в сторону "Сервер -> Клиент". Протокол, стоящий выше TCP-уровня, при этом не специфицируется и может быть любым (например HTTP). Мультиплексор имеет одну публичную входную линию WAIT, к которой присоединяются сотни тысяч клиентов, а также одну внутреннюю входную линию IN, через которые поступают асинхронные события от Сервера, раздаваемые затем ждущим клиентам. После того как клиент послал запрос и получил ответ по линии WAIT, он сразу же отсоединяется, но может при необходимости снова присоединиться ко входной линии WAIT для дальнейшего ожидания новых команд. Главная идеология +-------------------+ ------------------ | Сервер обработки | | | <===WAIT=== Клиент A | и базы данных | ======IN=======> | Мультиплексор | <===WAIT=== Клиент B | (e.g Apache + PHP | | | <===WAIT=== Клиент C +-------------------+ ------------------ (указаны направления установления TCP-соединений). Каждой линии выделен отдельный TCP-порт (указываются в конфигурации). Например, линия WAIT может иметь порт 8088, а линия
Таким образом, Мультиплексор отвечает за однонаправленную передачу данных от Сервера к Клиентам, позволяя при этом обслуживать значительное количество одновременных открытых соединений, в то время как на Сервере соединений открыто мало.
Идентификация КлиентаКаждый присоединенный Клиент имеет идентификатор, представляющий собой алфавитно-цифровую строку (обычно это MD5-код). Несколько различных Клиентов могут иметь один и тот же идентификатор: в этом случае данные, передаваемые с Сервера, поступят в каждый из этих Клиентов. Протокол запроса линии WAITКлиент присоединяется к линии WAIT (например, при помощи XMLHttpRequest) и передает в нее массив байтов. Внутри этого массива должен содержаться идентификатор Клиента в формате, определяемом следующим регулярным выражением: \bidentifier=([a-zA-Z0-9_]+)\W Мультиплексор анализирует байты на наличие данной последовательности. Это позволяет использовать Мультиплексор
для практически любого вышестоящего протокола: например, в случае HTTP-протокола идентификатор можно прислать в
теле GET-запроса: Мультиплексор запоминает, что данное соединение связано с Клиентом посредством указанного идентификатора. Все приходящие по линии IN данные, снабженные указанным идентификатором, будут переправлены соответствующим Клиентам. Идентификатор должен встретиться в первых WAIT_MAXLEN байтах запроса, в противном случае соединение принудительно разрывается. Протокол ответа линии WAITФормат данных никак не специфицируется. Данные извлекаются из очереди и передаются Клиенту в том виде, в котором они поступили от Сервера, без изменений и анализа. (Например, в случае HTTP-протокола это может быть обычный HTTP-ответ.) Если очередь пуста, Мультиплексор ожидает ее пополнения и, как только данные пришли, отправляет их Клиенту. Если данные не приходят в течение WAIT_TIMEOUT секунд, Мультиплексор принудительно закрывает соединение, не отправляя никаких данных Клиенту. После отправки ответа Мультиплексор закрывает соединение с Клиентом. Протокол линии INЛиния IN используется для передачи как данных, так и команд в Мультиплексор. Линия IN: передача данных для КлиентаПротокол практически идентичен протоколу запроса линии WAIT: Сервер передает данные для Клиента, снабжая их идентификатором Клиента прямо в теле данных. Весь обмен может занимать не более IN_TIMEOUT секунд, а размер запроса не может превышать IN_MAXLEN байтов. Имеется одно важное отличие протокола линии IN от протокола WAIT: в линии IN можно указывать сразу несколько ID Клиентов через запятую. В нормальной ситуации Мультиплексор никогда не разрывает соединение с Сервером самостоятельно. Вместо этого он считает отключение Сервера сигналом о завершении данных и начинает передавать команды Клиентам. Таким образом, Сервер должен определять, успешно ли произошло общение с Мультиплексором, просто проверяя: удалось ему закрыть соединение самостоятельно или нет. Линия IN: ONLINE: запрос списка online-КлиентовЧтобы получить список online-Клиентов, нужно послать в линию IN строчку: "ONLINE\n". Ответ на этот запрос может быть прочитан из линии IN. Он представляет собой список идентификаторов через запятую, в конце которого идет точка "." на отдельной строке. Точка может использоваться вызывающей стороной для верификации окончания передачи.
Автоматическая очистка очереди и неактивные онлайн-КлиентыВначале немного терминологии.
Сразу же после подключения Клиент объявляется online-Клиентом. Если Клиент не переподключился в течение OFFLINE_TIMEOUT секунд после своего последнего отсоединения, он объявляется offline-Клиентом. Для offline-клиентов очередь команд очищается. Кроме того, попытка добавить новую команду для offline-клиента вызывает ее игнорирование. Данное поведение предотвращает переполнение очереди команд, а также позволяет отслеживать внутри Мультиплексора,
какие Клиенты находятся в логическом состоянии online, а Конфигурация и расположениеОсновной файл Мультиплексора располагается в dklab_multiplexor.pl. При его запуске происходит считывание двух конфигурационных файлов:
Во втором файлы можно переопределить параметры, специфичные для продакшен-окружения. Потребление ресурсовМультиплексор может слушать как один, так и несколько IP-адресов и портов в системе. Адреса и порты задаются в директива WAIT_ADDR. Указав несколько IP-адресов, можно принимать огромное число одновременных входящих соединений (сотни тысяч и миллионы) в рамках одной машины. Расход памяти Мультипдексора составляет примерно 10М на тысячу открытых соединений. Мультиплексор не использует многопоточность, а вместо этого применяет библиотеку libevent (или ее аналоги) для событийно-ориентированной обработки соединений. Это дает значительную экономию памяти и процессорных ресурсов по сравнению с многопоточной версией. К сожалению, библиотека Perl Event::Lib не может работать в многопоточном режиме, поэтому в случае наличия нескольких процессоров в системе Мультиплексор будет использовать только один из них. В будущих версиях Event::Lib это обещают исправить, так что можно будет доработать Мультиплексор для полного использования ресурсов процессора. Чтобы снять на число открытых сокетов (и файлов), установленное по умолчанию в некоторых ОС равным 1024, сразу после запуска Мультиплексор выполняет команду: ulimit -n 1048576 Сопряжение с балансеромДемон Мультиплексора по умолчанию слушает порт 8088. Чтобы получить к нему доступ, на балансере можно использовать правило: переадресовывать GET-запросы на /multiplexor* на 127.0.0.1:8080. Таким образом, к Мультиплексору можно обращаться, например, по такому адресу: http://example.com/multiplexor?identifier=abcd ТестированиеЧтобы проверить, как работает Мультиплексор, надо вначале внимательно прочитать и понять его документацию. После чего можно идти браузером (или сразу несколькими разными браузерами, чтобы проэмулировать многооконность; только не ходите в разных табах одного браузера, т.к. табы иногда не дают устанавливать несколько одновременных соединений с одним и тем же урлом) на http://example.com/multiplexor?identifier=abcd Браузер "подвиснет" в ожидании события. Затем на сервере быстро (пока не закончился тайм-аут) запустить: telnet localhost 10010 (это порт линии сервера) и в страшной спешке впечатать туда: HTTP 200 OK Content-type: text/html X-multilexor: identifier=abcd A message from the server! Затем нажать Ctrl+] и q (чтобы завершить соединение). Браузер должен "отвиснуть" и отобразить текст сообщения. Тут |