Pull to refresh

Реализация демона и его взаимодействия с PHP-приложением

Reading time 3 min
Views 1.2K
Собственно это скорее полутопик-полувопрос.
Я опишу схему, которая у меня получилась и попрошу у хабралюдей совета — насколько такая реализация имеет право на жизнь и что в ней стоит улучшить =)
Возможно кому то такая идея понравится и он возьмет ее на вооружение — я не против =)

Такая схема у меня используется в двух задачах, однако распишу все на примере одной.
Итак, у меня есть некоторый онлайн браузерный игровой проект. Задача стояла в том чтобы написать максимально быстродействующий чат, обладающий следующими свойствами:
— низкая нагрузка, создаваемая чатом на сервер;
— поддержка большого количества игроков;
— возможность работы как в непрерывном так и в обычном (рефреш) режимах;
— возможность работы у игроков за прокси-серверами.



Общая схема работы


Я решил выделить обработку очереди сообщений в отдельный процесс — демон чата.
С клиентом же работает либо php-скрипт (в случае рефреш-режима) либо CGI-приложение, написанное на C (для непрерывного режима).
В обоих случаях происходит получение сообщений у демона и отправка клиенту JS-кода, добавляющего сообщения в фрейм чата.
В рефреш-режиме запрос к этому php-скрипту уходит раз в несколько секунд, скрипт загружает все сообщения, появившиеся после последнего рефреша и отдает клиенту.
В непрерывном режиме CGI-процесс запускается, и в бесконечном цикле с небольшой задержкой проверяет очередь сообщений на предмет появления новых. Если появляются — сразу посылает их клиенту.

Интерфейс взаимодействия демона и клиентов


Для CGI-приложения оптимальным мне показался самым быстрым вариант разделяемого сегмента памяти.
Демон при запуске выделяет сегмент разделяемой памяти и пишет туда все сообщения.
У структуры, описывающей сообщение, фиксированный размер и простой бинарный формат. Поэтому получить сообщение по его порядковому номеру в очереди — задача простая.
Очередь сообщений циклическая, после того, как очередь полностью заполнена — новые сообщения начинают заполнять очередь сначала, затирая старые сообщения. Также в этом сегменте присутствуют по фиксированным смещениям два значения, показывающие текущие указатели начала и конца очереди.
CGI-клиент подключает при запуске этот сегмент памяти и очевидным образом читает оттуда данные, практически мгновенно узнавая о новых сообщениях.
Состояния гонки при этом не возникает, т.к. пишет в сегмент только демон. Читателей же может быть неограниченное количество.

Добавление сообщений в очередь


Для добавления сообщений в очередь клиентские скрипты подключаются через UNIX сокет к демону и посылают специальным образом сформированный пакет данных.

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

Плюсы реализации


Поставленная задача решена =)
Чат не использует нестандартных портов — все работает через веб-сервер, поэтому чат может работать за жесткими корпоративными прокси.
Нагрузка на сервер от чата практически неощутима.

Минусы реализации


Плохая масштабируемость, расчет на то, что веб-сервер и демон работают на одной машине.

Заключение


Данная схема работы обкатана на продакшне и замечаний не вызвала.
Однако возможно она неоптимальна — это лучшее из того что пришло в голову =)
Буду рад услышать ваши комментарии по такому варианту реализации.
Если вдруг кому-нибудь покажется интересной моя реализация — готов рассказать о чем-нибудь подробнее, например углубиться прикладную часть работы чата (каналы, фильтр, форматирование сообщений, команды) или в системную часть реализации сервера (с примерами кода).
Tags:
Hubs:
+4
Comments 25
Comments Comments 25

Articles