Skip to content

Конфигурация

Чтобы запустить Rapira, файл конфигурации не нужен: у rapira serve app/worker.php на всё найдётся значение по умолчанию. rapira.toml вы добавляете тогда, когда этих значений перестаёт хватать: нужен другой адрес, фиксированное число воркеров, политика пересоздания процессов, pidfile, который прочитает система инициализации, более подробный уровень логирования. Покажите серверу файл — и дальше он читает настройки оттуда:

bash
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".

toml
[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логическоеfalsefalse оставляет воркер живым между запросами (режим 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 внутри приложения и пишите пути относительно него. Тогда переезд каталога уносит с собой всю конфигурацию целиком, и ничто не зависит от того, из какого каталога служба оказалась запущена.