PHP-сервери з тривалим часом роботи

Налаштування дистрибутиву OpenTelemetry для PHP у Laravel Octane (Swoole, RoadRunner) та інших довготривалих серверних процесів PHP.

PHP-фреймворки, такі як Laravel Octane (з Swoole або RoadRunner), запускають PHP як довготривалий серверний процес замість створення нового процесу на запит. Це змінює поведінку distro в кількох важливих аспектах і вимагає специфічних налаштувань.

Як працюють традиційні PHP-сервери

З PHP-FPM або Apache mod_php кожен HTTP-запит відповідає власному життєвому циклу PHP-процесу:

  1. PHP-процес стартує → distro bootstrap (OTel SDK ініціалізовано, автоінструментаційні hook-и зареєстровані)
  2. Запит обробляється → створюються відрізки для HTTP-транзакції та будь-яких інструментованих викликів (curl, PDO, тощо)
  3. Відповідь відправлена → запускаються PHP shutdown-функції → відрізки очищаються та експортуються
  4. PHP-процес завершується

Відрізок транзакції distro (OTEL_PHP_TRANSACTION_SPAN_ENABLED) обгортає саме один HTTP-запит: він стартує, коли запит надходить (web SAPI, $_SERVER заповнено) і закінчується, коли процес виходить. Це кореневий відрізок, від якого висять всі дочірні відрізки (curl, DB запити, тощо).

Чим відрізняються сервери, що працюють безперервно

З Laravel Octane (Swoole або RoadRunner) один PHP worker-процес обробляє багато HTTP-запитів поспіль без виходу між ними:

  1. PHP-процес стартує — worker-процеси стартують з повністю ініціалізованим distro (SDK, hook-и, експортер)
  2. Кожен HTTP-запит диспетчеризується до worker-а — hook-и worker-а спрацьовують і створюються відрізки
  3. Відповідь відправлена — PHP shutdown-функції НЕ запускаються (процес продовжує працювати)
  4. Worker-процес виходить лише при зупинці сервера (graceful stop)

Оскільки worker-процес є CLI-процесом (запущеним через php artisan octane:start), SAPI завжди cli, а не fpm-fcgi або apache2handler. Distro використовує це для розрізнення:

  • OTEL_PHP_TRANSACTION_SPAN_ENABLED — кореневий відрізок для web SAPI (FPM/Apache). Не актуально тут.
  • OTEL_PHP_TRANSACTION_SPAN_ENABLED_CLI — кореневий відрізок для CLI-процесів. У сервері, що працює безперервно, це обгортає весь час життя сервера (від octane:start до octane:stop), а не окремий запит.
АспектPHP-FPM / ApacheСервер, що працює безперервно (Octane)
SAPIfpm-fcgi / apache2handlercli
Життєвий цикл процесуОдин процес на запитОдин worker обробляє багато запитів
PHP shutdown-функціїЗапускаються після кожного запитуЗапускаються лише при виході worker-а
Distro bootstrapЗапускається на кожному запитіЗапускається один раз на worker при старті
Відрізок транзакції (_CLI)Не застосовуєтьсяОбгортав би увесь час життя сервера — вимкніть його

Вимкніть CLI відрізок транзакції

Автоматичний кореневий відрізок (OTEL_PHP_TRANSACTION_SPAN_ENABLED_CLI) охоплює весь процес PHP. На сервері, що працює тривалий час, це означає, що один відрізок триватиме аж до вимкнення сервера, що не є корисною телеметрією.

export OTEL_PHP_TRANSACTION_SPAN_ENABLED_CLI=false

Вимкніть виведені відрізки

Виведені відрізки (вибірка трасування стека) призначені для традиційного PHP, що працює на основі запитів. На сервері з тривалим часом роботи вибірка виконується безперервно між запитами, що створює шум і споживає ресурси CPU.

export OTEL_PHP_INFERRED_SPANS_ENABLED=false

Це стандартне значення, тому потрібно лише якщо ви раніше увімкнули виведені відрізки глобально.

Процесор відрізків та експорт затримки

Зазвичай distro використовує BatchSpanProcessor, який накопичує відрізки в памʼяті і експортує їх за таймером (стандартно: кожні 5 секунд). PHP є однопотоковим, тому перевірка таймера запускається лише коли новий відрізок завершується через onEnd() — немає фонового tick. Завжди використовуйте graceful stop (php artisan octane:stop, SIGTERM), щоб worker-и могли завершити поточний запит, запустити PHP shutdown-функції і очистити експортер перед виходом. Жорстка зупинка (SIGKILL) обходить все це.

З graceful stop, відрізки, забуферовані в памʼяті, очищуються перед виходом процесу — за умови, що точка доступу OTLP доступна. Однак BatchSpanProcessor вводить затримку експорту, пропорційну частоті надходження запитів. В застосунку з низьким трафіком відрізок, створений о 10:00, може не зʼявитися в колекторі до 10:05 (наступний запит нарешті запустить перевірку таймера). Щоб отримати інформацію майже в режимі реального часу, скористайтеся SimpleSpanProcessor:

export OTEL_PHP_TRACES_PROCESSOR=simple

Кожен відрізок миттєво потрапляє в чергу експорту при onEnd(), незалежно від обсягу трафіку.

Вбудований транспорт C++ цього дистрибутиву (HttpTransportAsync) має власну внутрішню чергу та постійне з’єднання з точкою доступу OTLP, тому перехід на simple не означає один HTTP-запит на кожен відрізок. Рівень PHP синхронно передає дані до черги C++ (швидка операція в середині процесу), а рівень C++ обʼєднує їх у пакети та незалежно надсилає через постійне з’єднання.

Повний приклад

Та сама конфігурація застосовується як до Swoole, так і до RoadRunner:

export OTEL_SERVICE_NAME="my-laravel-octane-app"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"

# Налаштування для постійно працюючого сервера
export OTEL_PHP_TRANSACTION_SPAN_ENABLED_CLI=false
export OTEL_PHP_INFERRED_SPANS_ENABLED=false
export OTEL_PHP_TRACES_PROCESSOR=simple

# Swoole
php artisan octane:start --server=swoole

# RoadRunner
php artisan octane:start --server=roadrunner

Як інструментування працює для кожного типу сервера

Swoole

Swoole створює робочі процеси шляхом розгалуження головного процесу PHP. Дистрибутив завантажується один раз у головному процесі, а робочі процеси успадковують його стан ініціалізації (хуки, SDK, з’єднання з експортером). Кожен HTTP-запит у робочому процесі запускає зареєстровані хуки автоматичної інструментації (Laravel, curl, PDO тощо), а відрізки створюються в рамках TracerProvider робочого процесу.

RoadRunner

RoadRunner — це сервер застосунків на базі Go, який керує робочими процесами PHP. На відміну від Swoole, він не використовує відгалуження — кожен робочий процес PHP запускається як окремий процес. Як наслідок, кожен робочий процес самостійно завантажує дистрибутив під час запуску. З точки зору інструментування поведінка є ідентичною: SAPI робочого процесу — cli, він обробляє багато запитів, не завершуючи роботу між ними, і застосовується та сама конфігурація.

Затримка у розкладі BatchSpanProcessor

Якщо ви хочете зберегти BatchSpanProcessor але зменшити затримку експорту, зробіть меншою затримку у розкладі:

export OTEL_BSP_SCHEDULE_DELAY=500   # ms, типово 5000

Це допомагає лише в застосунках зі стабільним трафіком — таймер спрацьовує на onEnd(), так що тихіший застосунок все ще відчуває затримку, пропорційну проміжку між запитами.


Востаннє змінено July 12, 2026: [uk] Ukrainian documentation for OpenTelemetry (83a19542)