Конфигурация
Чтобы запустить Rapira, файл конфигурации не нужен: у rapira serve app/worker.php на всё найдётся значение по умолчанию. rapira.toml вы добавляете тогда, когда этих значений перестаёт хватать: нужен другой адрес, фиксированное число воркеров, политика пересоздания процессов, pidfile, который прочитает система инициализации, более подробный уровень логирования. Покажите серверу файл — и дальше он читает настройки оттуда:
rapira serve --config /etc/rapira/rapira.tomlВ файле четыре секции, и каждая из них необязательна: [http] настраивает слушающий сокет, [pool] — рабочие процессы, [supervisor] — мастер-процесс, [log] — то, что уходит в stderr. Единственный ключ без значения по умолчанию — входной PHP-скрипт: задайте его ключом pool.entrypoint или передайте позиционным аргументом в командной строке.
Настройки складываются слоями: флаг командной строки сильнее файла конфигурации, а файл сильнее встроенного значения по умолчанию. Поэтому --processes 8 перебивает processes = 4 из файла, и конфигурацию, которая лежит в системе контроля версий, всё равно можно переопределить на один запуск. Переменные окружения в этих слоях не участвуют: кроме двух, которые влияют только на логирование, настройки берутся из файла и флагов, и больше ниоткуда. Сами флаги описаны в разделе Командная строка.
Полный rapira.toml
Все ключи, которые понимает Rapira, собраны в одном файле. Обязательного здесь нет ничего: удалите любую строку, и вступит в силу значение по умолчанию. Исключений два — у pool.entrypoint значения по умолчанию попросту нет, а min_spare и max_spare обязательны, пока в файле задан mode = "dynamic".
[http]
listen = "127.0.0.1:8000"
server_name = "localhost" # optional; SERVER_NAME reported to PHP
server_port = 8000 # optional; defaults to the listen TCP port (80 for unix:)
max_body_size_mb = 8 # optional; larger request bodies get a 413
unsafe_field_names = "drop" # optional; drop (default) | reject
[pool]
entrypoint = "index.php" # relative → resolved against this file's directory
processes = 4 # worker processes to fork (max_children for mode = dynamic/ondemand)
classic = false # optional; default false
mode = "dynamic" # static (default) | dynamic | ondemand
min_spare = 1 # dynamic only: keep at least this many idle workers
max_spare = 3 # dynamic only: trim to at most this many idle workers (rejected under other modes)
max_requests = 0 # recycle a worker after N requests (+jitter); 0 = unlimited
process_idle_timeout_secs = 10 # ondemand: retire an idle worker after this long
request_terminate_timeout_secs = 0 # kill a worker whose single request runs longer (wall clock); 0 = off
[supervisor] # optional; master-process policy
pidfile = "/run/rapira.pid" # optional; relative paths resolve against this file's dir
process_control_timeout_secs = 30 # graceful-stop budget before QUIT → TERM → KILL
[log] # optional; verbosity and record shape
level = "error" # error (default) | warn | info | debug | trace
format = "plain" # plain (default) | json
[log.targets] # optional; per-target overrides on top of level
php = "debug"
pingora_core = "warn"Дальше на этой странице эти ключи разобраны по секциям.
Секция [http]
В этой секции — где Rapira слушает, что окружение запроса сообщает PHP о сервере, под которым он работает, и сколько тела запроса сервер прочитает.
| Ключ | Тип | По умолчанию | Что делает |
|---|---|---|---|
listen | строка | "127.0.0.1:8000" | Адрес, который занимает сервер. Допустимы три формы: host:port с IP-литералом (127.0.0.1:8000, [::1]:8000), :port для всех интерфейсов и unix:/run/rapira.sock для Unix-сокета. Голый номер порта и имя хоста отвергаются: адрес обязан называть конкретный интерфейс. |
server_name | строка | "localhost" | То, что PHP прочитает в $_SERVER['SERVER_NAME']. |
server_port | целое | порт из listen, 80 для unix: | То, что PHP прочитает в $_SERVER['SERVER_PORT']. Задайте его, когда прокси перед Rapira принимает соединения на одном порту, а Rapira слушает другой. |
max_body_size_mb | целое | 8 | Наибольшее тело запроса, которое Rapira согласится принять, в МиБ (1024 × 1024 байт). На всё, что больше, приходит ответ 413. Значение не меньше 1. |
unsafe_field_names | "drop" | "reject" | "drop" | Что делать с полем запроса, имя которого не укладывается в [A-Za-z0-9-]: выбросить его до того, как его увидит PHP, записав каждое удаление в лог на уровне warn, — или ответить 400. Зачем так сделано и как имена превращаются в переменные CGI, разбирает раздел HTTP. |
server_name и server_port влияют только на то, что PHP увидит в $_SERVER, и никак не меняют адрес, который занимает сервер: его задаёт только listen.
Секция [pool]
Воркеры — это процессы, которые непосредственно выполняют PHP, а секция говорит, что именно они выполняют, сколько их и когда мастер убирает одного из них. Как мастер распоряжается этими числами, объясняет Модель процессов.
| Ключ | Тип | По умолчанию | Что делает |
|---|---|---|---|
entrypoint | строка | нет — ключ обязателен | PHP-скрипт, который выполняет каждый воркер. Относительный путь считается от каталога с файлом конфигурации. Аргумент SCRIPT в командной строке перебивает этот ключ, и одно из двух должно быть задано, иначе сервер откажется стартовать. |
processes | целое | по одному на логическое ядро | Сколько рабочих процессов форкать. В режимах dynamic и ondemand это не количество, а потолок. Значение не меньше 1. |
classic | логическое | false | false оставляет воркер живым между запросами (режим SAPI Worker); true выполняет входной скрипт с нуля на каждый запрос, ровно как это делал бы php-fpm. Смотрите Режимы выполнения. Флаг --classic умеет только включать режим: true, записанный здесь, из командной строки уже не отменить. |
mode | "static" | "dynamic" | "ondemand" | "static" | Как пул подбирает себе размер. static всё время держит живыми processes воркеров; dynamic меняет их число между порогами свободных воркеров, не поднимаясь выше processes; ondemand форкает воркер только под работу и отпускает тех, кто простаивает. |
min_spare | целое | нет | Только для dynamic, и там обязателен: держать наготове не меньше такого числа свободных воркеров. |
max_spare | целое | нет | Только для dynamic, и там обязателен: сокращать число простаивающих воркеров до этого значения. Пара должна укладываться в 1 <= min_spare <= max_spare <= processes, а в любом другом режиме каждый из этих ключей — ошибка. |
max_requests | целое | 0 | Пересоздать воркер после того, как он обслужит столько запросов, плюс небольшой разброс, чтобы пул никогда не обновлялся весь разом. 0 — никогда. |
process_idle_timeout_secs | целое | 10 | Читается в режиме ondemand: сколько воркеру позволено простаивать, прежде чем мастер его уберёт. |
request_terminate_timeout_secs | целое | 0 | Сколько реального времени отводится на один запрос. Воркер, который к этому моменту всё ещё занят им, снимается и заменяется новым. 0 отключает проверку. |
Границы для свободных воркеров сверяются с действующим значением processes, поэтому флаг --processes в командной строке опускает и тот потолок, под который должен уместиться max_spare.
Секция [supervisor]
Правила для мастер-процесса — того самого, который держит слушающий сокет, присматривает за воркерами и принимает ваши сигналы. С ним же общается система инициализации, поэтому в unit-файле обычно задают именно эти ключи; смотрите Развёртывание.
| Ключ | Тип | По умолчанию | Что делает |
|---|---|---|---|
pidfile | строка | нет | Куда мастер записывает собственный pid. Относительный путь считается от каталога с файлом конфигурации. Именно этому pid и отправляют сигналы — полная таблица того, что делает каждый из них, есть в Модели процессов. |
process_control_timeout_secs | целое | 30 | Сколько мастер даёт воркеру на мягкое завершение, прежде чем пойти по цепочке QUIT → TERM → KILL. |
Секция [log]
Rapira пишет всё в stderr, по одной операции записи на сообщение, поэтому вывод мастера и воркеров не перемешивается посреди строки. Эта секция решает, насколько подробным будет этот поток и как выглядит каждая запись; про отдельные цели, форматы и про то, как диагностика PHP раскладывается по уровням, рассказывает Логирование.
| Ключ | Тип | По умолчанию | Что делает |
|---|---|---|---|
level | "error" | "warn" | "info" | "debug" | "trace" | "error" | Подробность записей, общая сразу для всех целей. |
format | "plain" | "json" | "plain" | Как выглядит запись: читаемые человеком строки (с цветом, когда stderr — терминал) или по одному JSON-объекту на строку для сборщика логов. |
[log.targets] | таблица «цель → уровень» | пусто | Точечные переопределения поверх level: например, php = "debug", пока всё остальное молчит. Ключ сопоставляется по префиксу, поэтому php захватывает заодно и php_sys::callbacks, и всё, что ниже. |
Ключ в [log.targets] должен выглядеть как путь модуля: буквы, цифры и _ : . -, а первым символом — буква, цифра или _. Из ключей собирается строка фильтра, поэтому всё, что выходит за эти рамки, было бы прочитано как синтаксис фильтра, а не как имя цели, — и отвергается сразу.
RUST_LOG и NO_COLOR — единственные переменные окружения, которые читает Rapira, и обе касаются только логов: RUST_LOG на один запуск заменяет весь фильтр целиком, так что ради шумной отладочной сессии не придётся править конфигурацию, а NO_COLOR убирает цвет из формата plain при любом непустом значении, даже когда stderr — терминал.
Незнакомые ключи отвергаются
Rapira разбирает rapira.toml строго. Каждая таблица и каждый ключ внутри неё должны быть серверу знакомы, поэтому [htttp] или lissten = ":8000" роняют запуск с сообщением о том, что именно не удалось распознать, а не тихо остаются без внимания. И у каждого ключа ровно одно место: max_requests живёт в [pool] и больше нигде, pidfile — в [supervisor] и больше нигде, а ключ под чужой таблицей ломает старт точно так же, как опечатка.
Со значениями всё устроено так же. level = "verbose", format = "pretty" и unsafe_field_names = "allow" — жёсткие ошибки, а не тихий откат к значению по умолчанию, так что опечатка не может незаметно ослабить защитную настройку. У чисел тоже есть границы: pool.processes и http.max_body_size_mb не меньше 1, а любой ключ *_secs ограничен сверху значением 86400 — это сутки.
Проверка идёт до того, как что-либо запустится, поэтому нераспознанный ключ останавливает старт, а не тихо ухудшает работу сервера. Правка rapira.toml на машине, которая прямо сейчас обслуживает трафик, не трогает работающий процесс, но следующий запуск обязан пройти успешно.
Относительные пути
Путь в файловой системе хранят два ключа — pool.entrypoint и supervisor.pidfile, — и оба считаются от каталога с файлом конфигурации, а не от рабочего каталога того, кто запустил сервер. Если файл лежит в /etc/rapira/rapira.toml и в нём написано entrypoint = "app/worker.php", скриптом будет /etc/rapira/app/worker.php, откуда бы ни вызвали rapira serve.
Позиционный аргумент SCRIPT устроен наоборот. Это значение из командной строки, поэтому относительный путь в нём считается от текущего рабочего каталога.
Держите rapira.toml внутри приложения и пишите пути относительно него. Тогда переезд каталога уносит с собой всю конфигурацию целиком, и ничто не зависит от того, из какого каталога служба оказалась запущена.