Laravel Octane: Swoole и RoadRunner
Что такое Octane
Octane — сервер приложений, который держит Laravel в памяти между запросами.
Что даёт:
- Bootstrap один раз.
- Отклик в разы быстрее.
- RPS в 5–10× выше.
- Память — константна.
Что стоит:
- Сложнее дебажить.
- Утечки заметны.
- Не всё работает.
Правило: Octane — следующий шаг после оптимизаций.
Как работает
Обычный PHP-FPM:
- Каждый запрос — bootstrap.
- Composer, config, providers.
- 50–100 мс на пустой запрос.
Octane:
- Bootstrap один раз.
- Keep-alive воркеры.
- 1–3 мс на пустой запрос.
Правило: Octane экономит bootstrap.
Два сервера
- Swoole — расширение PHP.
- RoadRunner — Go-сервер.
Правило:
- Swoole — максимальная скорость.
- RoadRunner — проще ставить.
Требования
Swoole:
- PHP 8.1+.
- Расширение swoole.
- Linux / macOS.
RoadRunner:
- PHP 8.1+.
- Бинарник rr.
- Linux / macOS / Windows.
Установка
composer require laravel/octane
php artisan octane:install
Что спрашивает:
- Swoole или RoadRunner.
- Создать конфиг
config/octane.php.
Swoole: установка расширения
pecl install swoole
php.ini:
extension=swoole.so
Проверка:
php -m | grep swoole
Правило: Swoole — не через composer, а через pecl.
RoadRunner: установка
./vendor/bin/rr get-binary
Что даёт: бинарник rr в проекте.
Запуск
Dev:
php artisan octane:start --watch
Прод (Swoole):
php artisan octane:start --server=swoole --host=0.0.0.0 --port=8000 --workers=4
Прод (RoadRunner):
php artisan octane:start --server=roadrunner --host=0.0.0.0 --port=8000 --workers=4
Правило: --workers = количество CPU-ядер.
Reload
php artisan octane:reload
php artisan octane:stop
Что даёт: перезагрузка воркеров без простоя.
Правило: после деплоя — всегда octane:reload.
Что важно
Проблемы:
- Состояние между запросами.
- Статичные свойства не сбрасываются.
- Singleton живёт долго.
- Утечки памяти.
- Соединения к БД не закрываются.
Правило: писать stateless код.
Stateless код
Плохо:
class UserService
{
private ?User $currentUser = null;
public function setUser(User $user): void { /* ... */ }
}
Хорошо:
class UserService
{
public function get(User $user): array { /* ... */ }
}
Правило: не хранить состояние в сервисах.
Config cache
Обязательно:
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
Правило: Octane требует кэша.
Не сбрасывается автоматически
Что остаётся:
- Статичные свойства.
- Container singletons.
- App state.
- Статические счётчики.
Правило: за этим следить вручную.
Listeners
config/octane.php:
'listeners' => [
RequestReceived::class => [
...Octane::prepareApplicationForNextOperation(),
...Octane::prepareApplicationForNextRequest(),
],
RequestTerminated::class => [
...Octane::prepareApplicationForNextOperation(),
],
],
Что делают: сбрасывают состояние.
Правило: использовать штатные listeners.
Утечки памяти
Как найти:
use Laravel\Octane\Events\RequestTerminated;
Event::listen(RequestTerminated::class, function () {
if (memory_get_usage() > 100 * 1024 * 1024) {
Log::warning('Memory leak', [
'usage' => memory_get_usage(true),
'url' => request()->fullUrl(),
]);
}
});
Правило: логировать при > 100 MB.
Restart по памяти
config/octane.php:
'max_requests' => 500, // после — воркер перезапускается
Что даёт: утечки не накапливаются.
Правило: max_requests — обязательно.
Swoole Table
Для shared state:
use Swoole\Table;
$table = new Table(1024);
$table->column('count', Table::TYPE_INT);
$table->create();
$table->incr('key', 'count');
Что даёт: быстрый shared counter.
Правило: shared state — через Swoole Table.
Swoole Cache driver
.env:
CACHE_STORE=octane
Что даёт: кэш в памяти воркеров.
⚠️ Не шарится между воркерами.
Правило: octane-кэш — только для одного воркера.
Concurrent tasks
Swoole:
use Laravel\Octane\Facades\Concurrently;
[$users, $posts] = Concurrently::run([
fn () => User::all(),
fn () => Post::all(),
]);
Что даёт: параллельные запросы.
Правило: независимые операции — параллельно.
RoadRunner vs Swoole
| Swoole | RoadRunner | |
|---|---|---|
| Установка | pecl | бинарник |
| Скорость | Быстрее | Быстро |
| PSR-7 | Не полностью | Полностью |
| Windows | Нет | Да |
| Стабильность | Высокая | Высокая |
Правило:
- Linux → Swoole.
- Windows → RoadRunner.
- Простота → RoadRunner.
Octane vs PHP-FPM
Когда Octane:
- RPS > 1000.
- Bootstrap > 30 мс.
- Много запросов.
- Команда готова к нюансам.
Когда НЕ Octane:
- Маленький проект.
- Не готовы следить за утечками.
- Legacy с shared state.
Правило: Octane — не серебряная пуля.
Nginx + Octane
Upstream:
upstream octane {
server 127.0.0.1:8000;
keepalive 32;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://octane;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
}
}
Правило: keepalive — обязателен для скорости.
Supervisor
[program:octane]
command=php /var/www/skills/api/artisan octane:start --server=swoole --workers=4
user=www-data
numprocs=1
autorestart=true
stopwaitsecs=3600
Правило: Octane — один процесс на сервер.
Deploy
php artisan down
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan optimize:clear
php artisan optimize
php artisan octane:reload
php artisan up
Правило: reload — обязательно после деплоя.
Мониторинг
Что смотреть:
- Memory per worker.
- Restart count.
- RPS.
- p95 latency.
- Error rate.
Через Laravel Pulse + Sentry.
Правило: без мониторинга — утечка словит прод.
Практика
1. Установите Octane + Swoole.
2. Запустите octane:start --watch.
3. Проверьте X-Cache / X-Octane.
4. Замерьте latency до/после.
5. Включите max_requests = 500.
6. Логируйте при > 100 MB.
7. Проверьте на утечки.
8. Настройте Nginx + keepalive.
9. Supervisor + autorestart.
10. octane:reload в деплое.
Частые ошибки
1. Shared state:
Чужие данные
Решение: stateless.
2. Singleton с состоянием:
Утечка
Решение: reset в listener.
3. Нет max_requests:
OOM
Решение: 500.
4. Забыли reload:
Старый код
Решение: в деплой.
5. Без keepalive:
Медленно
Решение: Nginx upstream.
6. Octane на Windows:
Не работает
Решение: RoadRunner или Linux.
7. Не то ядро/расширение:
Ошибки
Решение: Swoole 5+, PHP 8.1+.
8. Без мониторинга:
Тихий OOM
Решение: Pulse + Sentry.
Итог
- Octane — долгоживущие воркеры.
- 5–10× быстрее.
- Swoole — быстрее.
- RoadRunner — проще.
- Stateless — обязательно.
max_requests— защита.octane:reload— после деплоя.- Nginx + keepalive.
- Мониторить память.
- Не серебряная пуля.
Дальше — оптимизация middleware и сервис-провайдеров.