Реализация демона и его взаимодействия с PHP-приложением
Собственно это скорее полутопик-полувопрос.
Я опишу схему, которая у меня получилась и попрошу у хабралюдей совета — насколько такая реализация имеет право на жизнь и что в ней стоит улучшить =)
Возможно кому то такая идея понравится и он возьмет ее на вооружение — я не против =)
Такая схема у меня используется в двух задачах, однако распишу все на примере одной.
Итак, у меня есть некоторый онлайн браузерный игровой проект. Задача стояла в том чтобы написать максимально быстродействующий чат, обладающий следующими свойствами:
— низкая нагрузка, создаваемая чатом на сервер;
— поддержка большого количества игроков;
— возможность работы как в непрерывном так и в обычном (рефреш) режимах;
— возможность работы у игроков за прокси-серверами.
Общая схема работы
Я решил выделить обработку очереди сообщений в отдельный процесс — демон чата.
С клиентом же работает либо php-скрипт (в случае рефреш-режима) либо CGI-приложение, написанное на C (для непрерывного режима).
В обоих случаях происходит получение сообщений у демона и отправка клиенту JS-кода, добавляющего сообщения в фрейм чата.
В рефреш-режиме запрос к этому php-скрипту уходит раз в несколько секунд, скрипт загружает все сообщения, появившиеся после последнего рефреша и отдает клиенту.
В непрерывном режиме CGI-процесс запускается, и в бесконечном цикле с небольшой задержкой проверяет очередь сообщений на предмет появления новых. Если появляются — сразу посылает их клиенту.
Интерфейс взаимодействия демона и клиентов
Для CGI-приложения оптимальным мне показался самым быстрым вариант разделяемого сегмента памяти.
Демон при запуске выделяет сегмент разделяемой памяти и пишет туда все сообщения.
У структуры, описывающей сообщение, фиксированный размер и простой бинарный формат. Поэтому получить сообщение по его порядковому номеру в очереди — задача простая.
Очередь сообщений циклическая, после того, как очередь полностью заполнена — новые сообщения начинают заполнять очередь сначала, затирая старые сообщения. Также в этом сегменте присутствуют по фиксированным смещениям два значения, показывающие текущие указатели начала и конца очереди.
CGI-клиент подключает при запуске этот сегмент памяти и очевидным образом читает оттуда данные, практически мгновенно узнавая о новых сообщениях.
Состояния гонки при этом не возникает, т.к. пишет в сегмент только демон. Читателей же может быть неограниченное количество.
Добавление сообщений в очередь
Для добавления сообщений в очередь клиентские скрипты подключаются через UNIX сокет к демону и посылают специальным образом сформированный пакет данных.
Демон при запуске создает несколько потоков и начинает слушать сокет.
Приходящие соединения раздает потокам по очереди.
Потоки принимают команду из сокета. Если это команда добавления сообщения в очередь — захватывают мьютекс, контролирующий обращение к очереди сообщений, и добавляют его туда.
Плюсы реализации
Поставленная задача решена =)
Чат не использует нестандартных портов — все работает через веб-сервер, поэтому чат может работать за жесткими корпоративными прокси.
Нагрузка на сервер от чата практически неощутима.
Минусы реализации
Плохая масштабируемость, расчет на то, что веб-сервер и демон работают на одной машине.
Заключение
Данная схема работы обкатана на продакшне и замечаний не вызвала.
Однако возможно она неоптимальна — это лучшее из того что пришло в голову =)
Буду рад услышать ваши комментарии по такому варианту реализации.
Если вдруг кому-нибудь покажется интересной моя реализация — готов рассказать о чем-нибудь подробнее, например углубиться прикладную часть работы чата (каналы, фильтр, форматирование сообщений, команды) или в системную часть реализации сервера (с примерами кода).