Асинхронное программирование в PHP (phpDaemon: пристегните ремни взлетаем) Основная секция
Тезисы
Тезисы:
Добрый день. Я расскажу о необычных аспектах применения PHP: о написании асинхронных сетевых приложений, и полу-синхронных веб-приложений.
Вы наверняка уже наслышаны о протоколе FastCGI, который служит каналом связи с приложением. Популярное применение связь между веб-сервером и Веб-приложением, его реализации есть во множестве языков. Протокол поддерживается многими веб-серверами, среди них nginx и lighttpd. Однако, в популярном языке PHP, реализация протокола до не столь давних пор была представлена лишь патчем PHP-FPM, который де-факто не выигрывает у Apache-prefork mod_php, поскольку он, вопреки идеологии FastCGI, запускает скрипт каждый раз с нуля, не говоря уже об асинхронности I/O. Идеология же FastCGI подразумевает единоразовую инициализацию приложения (подгрузку нужных библиотек, соединение с базой данных, и т.д.), и выполнение запросов один за другим или, вообще, параллельно (событийно переключаясь между контекстами запросов).
Модуль FastCGI в phpDaemon правильная реализация FastCGI, которая позволяет добиться немыслимой производительности и снижения нагрузки на систему. Теперь нам не нужно тысячи раз в секунду копировать мегабайты памяти, не надо каждый раз соединяться с базой данных и проходить у нее авторизацию... мы можем просто выполнить то, что действительно необходимо, и приняться за следующий запрос. Теперь не нужно думать о том, сколько у нас подгружается классов из тяжелого фреймворка. Кэш оп-кода (eAccelerator) даст прирост лишь в случае использования компилирующих шаблонизаторов.
Давайте для начала разберём архитектуру как же всё это работает, а затем я расскажу о предоставляемых вкусностях, и мы уделим значительное время вашим вопросам.
phpDaemon это вполне самостоятельный демон, способный обрабатывать соединения по нескольким TCP- и UNIX-сокетам. Он использует libevent.
Для начала работы вам необходимо создать файл с приложением в папке applications и выполнить phpdaemon start.
Мастер-процесс создаст нужное количество рабочих процессов, которые начнут принимают новые соединения в пул, читать из них данные и делегировать выполнение запросов нужным приложениям, руководствуясь указаниями AppResolver'а. При этом вы можете выполнять какие-то процедуры с запросом даже до окончания обработки тела запроса.
Строго говоря, операции принятия соединения, приема данных, обработки запроса, и передачи ответа асинхронны, т. е. мы можем принять два или больше соединений и поочередно их читать и обрабатывать в рамках одного процессе PHP (количество в рамках лимита max-concurrent-requests-per-worker). Разумеется, выполнение более одного конкурентного запроса в рабочем процесса потребует от программиста хранить все запросо-зависимые данные в экземпляре класса Request, и обеспечить отсутствие блокировок процесса. Это позволяет выполнять десятки тысяч запросов в секунду. Архитектура в чем-то похожа на nginx.
Архитектура позволяет легко создавать свои событийные сетевые сервисы, в дистрибутиве поставляются реализации FlashPolicy, HTTP, FastCGI, LockServer, TelnetHoneyPot, MemcacheClient, MongoClient...
Не станем вдаваться в подробности, но это действительно легко, и работать придётся лишь с оберткой, не думая о низком уровне.
Слайд №1: Спектр возможностей очень широк (всё опциально):
- Умный MPM.
- Модульная структура и API. /*К примеру, можно написать свой менеджер процессов, учитывающий фазу луны.*/
- Возможность смены пользователя и корня рабочих процессов.
- Поддержка Request-Body-File, X-Sendfile, multipart, attachment (callback), UCS-2.
- Прием APPNAME.
- Авто-сборка мусора (GC).
- Лимиты для рабочего процесса.
- Статус рабочих процессов.
- Асинхронность выполнения запросов.
- Асинхронная работа с сетью (libevent).
- Встроенный модуль асинхронного CGI-сервера (libevent).
Слайд №2: Где и как это можно применить особенно эффективно?
- Любые системы, использующие PHP.
- Сетевые Веб-приложения (IM-гейты, чаты).
- Торрент-трекеры.
Хотите немного серверной магии? Представьте себе несложную задачу: реализовать простецкую загрузку видео-файлов по HTTP, конвертацию их в FLV, и, разумеется, сохранение. Вы наверняка напишете простейший скрипт с использованием move_uploaded_file() и exec(), который будет висеть пока клиент отправляет запрос, кто-то пойдет дальше и будет записывать в БД идентификаторы файлов ожидающих конвертации, используя для конвертации отдельную пачку процессов, это позволит ограничить количество одновременно конвертируемых файлов.
А теперь я хочу немного вас удивить, я готов реализовать всю эту схему используя лишь 1 процесс PHP (ну, строго говоря, 2, учитывая мастер-процесс, но он не участвует в обработке). При этом не будет никакой потери в производительности, и мне даже не понадобится база данных (хотя, в нее стоит сбрасывать состояние, чтобы защититься от сбоев питания). Как? Очень просто. Достаточно создать приложение с использованием phpDaemon: в run() методе класса Request проверять счетчик работающих процессов конвертацию (обычную переменную), и если лимит не превышен открывать pipe'а с ffmpeg'ом и класть его в свойство запроса, ну и вызвать $this->sleep(), при повторном вызове проверять состояние pipe и в случае его завершения - завершать запрос (я имею в виду именно запрос в очереди рабочего процесса, броузер получит страничку и отключится задолго до этого).
Как это работает? Очень просто. Как я уже говорил, обработка запросов асинхронна, и рабочий процесс будет принимать сразу несколько upload'ов и раскидывать кусочки по временным файлам, а когда запрос полностью принят будет мгновенно открывать pipe c ffmpeg и возвращаться к пресловутой обработке.
Кстати, по такому же принципу работает встроенный CGI-сервер, служащий для подключения сторонних приложений.
Целевая аудитория:
Разработчики, в первую очередь так или иначе использующие PHP, но и все остальные тоже. Доклад позволит многим выйти на новую ступень оптимизации своих веб-проектов.
О докладчике:
Программист, системный архитектор, эксперт в области IT-безопасности. Опыт более 10 лет. Работаю CTO в Dultonmedia (http://yatv.ru/).