Skip to content

Режимы выполнения

Rapira запускает PHP в одном из четырёх режимов выполнения. Два из них уже доступны, остальные два запланированы.

РежимСтатусОписание
ClassicГотовоВходной скрипт выполняется с нуля на каждый запрос, как под php-fpm.
SAPI WorkerГотовоРезидентный скрипт стартует один раз и обрабатывает запросы в цикле; суперглобальные переменные заново заполняются для каждого запроса.
PSR WorkerВ планахВоркер забирает каждый запрос через вызов API и может работать с PSR-7-сообщением вместо суперглобальных переменных.
AsyncВ планахВоркер обрабатывает несколько запросов конкурентно в одном интерпретаторе, с помощью файберов.

Режимы перечислены в порядке того, сколько контроля над жизненным циклом запроса получает PHP. Названия говорят, живёт ли воркер между запросами и по какому контракту он общается с сервером. Каждый следующий режим оставляет к приходу запроса больше прогретого процесса, чем предыдущий, и предъявляет больше требований к коду.

Classic готово

Входной скрипт выполняется с нуля на каждый запрос — ровно так же, как под php-fpm: заполняются суперглобальные переменные, стартует фронт-контроллер, уходит ответ, и всё разбирается обратно. Созданное скриптом не переносится дальше, поэтому состояние приложения не протечёт из одного запроса в следующий. Исключения ровно те же, что и у php-fpm: постоянные соединения и состояние внутри расширений живут в процессе воркера, а не в запросе.

Готовое приложение работает как есть, потому что Rapira встаёт на место php-fpm и править код не нужно. PHP встроен прямо в процесс сервера, поэтому между HTTP-фронтом и интерпретатором нет промежуточного шага через FastCGI.

Подробности — в разделе Классический режим.

SAPI Worker готово

Режим SAPI Worker устроен так же, как Classic: вы по-прежнему читаете суперглобальные переменные и по-прежнему отдаёте ответ через echo. Разница в том, что в конце запроса воркер не завершается. Резидентный скрипт один раз поднимает всё окружение и уходит в цикл: сервер на каждый новый запрос заново заполняет $_GET, $_POST, $_SERVER, $_COOKIE и остальные суперглобальные переменные, вызывает ваш обработчик и передаёт ему следующий запрос. Автозагрузчик, DI-контейнер, конфигурация, соединения с базой — всё, что создано вне цикла, остаётся прогретым.

Старт выполняется один раз на воркер, а не один раз на запрос, а для современного приложения именно старт обычно и оказывается самой дорогой частью запроса. Процесс больше не начинает каждый запрос с чистого состояния, поэтому всё, что приложение оставило в статических свойствах, синглтонах или глобальном состоянии, никуда не денется к следующему запросу. Rapira умеет перезапускать воркер после заданного числа запросов, чтобы медленная утечка в приложении или в одной из его зависимостей не превратилась в аварию, пока вы её ищете.

Про сам скрипт воркера и его цикл читайте в разделе Режим воркера, про ограничение на число запросов до перезапуска — в разделе Конфигурация, а про работу с запросами и ответами — в разделе HTTP.

PSR Worker в планах

Управление инвертируется: вместо того чтобы ждать вызова, воркер сам забирает запрос у Rapira через вызов API и решает, что с ним делать. Можно заполнить суперглобальные переменные ради совместимости, а можно обойтись без них совсем и работать с PSR-7-сообщением, отдав его напрямую в HTTP-ядро фреймворка. Запросы обрабатываются по одному за раз, как и в SAPI Worker.

Запрос перестаёт быть глобальным состоянием и становится обычным значением: его можно передать дальше, обернуть или пропустить через стек middleware.

Режим PSR Worker не реализован. Сегодня из него ничего не работает, конфигурация и API на стороне PHP ещё не спроектированы, так что показывать имена функций и ключи конфигурации пока нечего.

Async в планах

Режим Async использует тот же API, что и PSR Worker, только воркер запрашивает сразу несколько запросов и обрабатывает их конкурентно внутри одного интерпретатора. Возможным это делают файберы из PHP 8.1: запрос, который ждёт ввода-вывода, уступает управление, пока продвигается другой, — без потоков и без второго процесса.

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

Режим Async тоже не реализован. Устанавливать и настраивать нечего. Раздел выше описывает планируемое направление, а не то, что уже можно запустить.

Выбор режима

По умолчанию Rapira работает в режиме SAPI Worker, а Classic включается явно. Все четыре режима открыты любому приложению, а ограничивает выбор его собственный стек. Глобальное состояние, которое не переживёт второй запрос, оставляет приложение на Classic. Библиотека, не умеющая работать в файберах, исключает Async. Фреймворк с готовой интеграцией с рантаймом даёт режим SAPI Worker почти без усилий; фреймворки с описанной интеграцией собраны в разделе Фреймворки.

Режим задаётся на весь экземпляр сервера, а не на отдельный маршрут, поэтому один экземпляр не может обслуживать часть маршрутов из воркера, а остальные — в Classic. Если часть приложения не готова к работе в воркере, вынесите её за отдельный экземпляр Rapira в режиме Classic.

Переход на воркерный режим стоит работы на стороне PHP: воркеру нужен резидентный входной скрипт, которого у Classic нет. Обратный переход не стоит ничего — включите Classic флагом в командной строке или одним ключом в конфигурационном файле, направьте Rapira на привычный фронт-контроллер, и вы получите тот же сервер, тот же бинарник и ту же модель процессов под ним. Подробности — в разделах Конфигурация и Командная строка.

Если вы заменяете php-fpm и хотите сначала просто увидеть работающее приложение, начинайте с Classic. Переходите на SAPI Worker, когда убедитесь, что приложение стартует чисто и не хранит между запросами состояния, которого хранить не должно.