Backend-разработка

Laravel Octane и Swoole в 2026 году: как кратно ускорить PHP-приложение без смены стека

Ruslan Ismailov Опубликовано 11 мин чтения
L

Введение: почему классический PHP-FPM тормозит

Большинство Laravel-приложений по-прежнему работают на связке Nginx + PHP-FPM. Это надёжная, проверенная схема — но у неё есть принципиальное ограничение: каждый HTTP-запрос порождает полный жизненный цикл PHP. Интерпретатор загружает файлы, инициализирует фреймворк, поднимает сервис-контейнер, обрабатывает роуты, отдаёт ответ — и уничтожает всё созданное. При следующем запросе процесс повторяется с нуля.

На практике это означает, что значительная часть процессорного времени и памяти тратится не на бизнес-логику, а на бутстрап фреймворка. Для высоконагруженных приложений, где счёт идёт на тысячи запросов в секунду, это становится узким местом, которое нельзя устранить только масштабированием железа.

Laravel Octane решает эту проблему принципиально иначе: приложение загружается один раз и остаётся в памяти, обслуживая запросы без повторной инициализации. В 2026 году этот подход из экзотики превратился в стандартную практику для production-окружений с требованиями к производительности.

Как работает Laravel Octane: резидентный процесс и жизненный цикл запроса

Laravel Octane — это официальный пакет от команды Laravel, который запускает приложение как долгоживущий процесс поверх высокопроизводительного сервера: Swoole или RoadRunner. Принципиальное отличие от PHP-FPM — в том, что фреймворк стартует один раз при запуске воркера.

Жизненный цикл выглядит так:

  1. Воркер стартует, Laravel загружается: регистрируются сервис-провайдеры, собирается контейнер, биндятся зависимости.
  2. Приходит HTTP-запрос. Octane клонирует состояние приложения (sandbox), передаёт управление роутеру.
  3. Роутер выполняет контроллер, возвращает ответ клиенту.
  4. Octane сбрасывает состояние sandbox, но не выгружает фреймворк. Следующий запрос обрабатывается тем же воркером без повторного бутстрапа.

Именно этот «клон и сброс» (fork-reset) — ключевое место, требующее внимания разработчика. Глобальное состояние, не сброшенное между запросами, приведёт к утечкам данных между пользователями. Об этом подробнее в разделе о ловушках.

Swoole vs RoadRunner: сравнение драйверов в 2026 году

Laravel Octane поддерживает два основных драйвера. Выбор между ними — один из первых вопросов при настройке.

Swoole

Swoole — это PHP-расширение, написанное на C, которое добавляет в PHP асинхронные примитивы: корутины, каналы, таймеры, TCP/HTTP-серверы. Именно Swoole лежал в основе Octane с момента его появления и до сих пор остаётся самым производительным вариантом в синтетических бенчмарках.

  • Встроенный HTTP-сервер работает без Nginx в качестве front-proxy (хотя на проде Nginx всё равно рекомендуется).
  • Поддержка корутин позволяет делать неблокирующие вызовы к базам данных и Redis прямо из PHP.
  • Требует установки расширения — это чуть сложнее в Docker, но решается одной строкой в Dockerfile.

RoadRunner

RoadRunner — это сервер на Go, который общается с PHP-воркерами по бинарному протоколу. Не требует PHP-расширений, устанавливается как отдельный бинарник. В 2026 году RoadRunner v3 стал значительно стабильнее и получил нативную поддержку gRPC, temporal-воркеров и WebSocket.

  • Проще в установке и отладке на нестандартных окружениях.
  • Лучше интегрируется с экосистемой Go-утилит (Prometheus-метрики, трейсинг).
  • На чистом HTTP чуть уступает Swoole по RPS, но разница на реальных приложениях нивелируется I/O-операциями.

Рекомендация 2026: если ваша команда работает в чистом PHP-стеке и нужна максимальная производительность HTTP — берите Swoole. Если важна расширяемость, нестандартные протоколы или вы уже используете Go-инфраструктуру — RoadRunner.

Установка и настройка Laravel Octane со Swoole в Docker

Ниже — пошаговый гайд, который работает на Laravel 11+ и PHP 8.3.

Dockerfile

FROM php:8.3-cli-alpine

RUN apk add --no-cache \
    linux-headers \
    $PHPIZE_DEPS \
    && pecl install swoole \
    && docker-php-ext-enable swoole \
    && apk del $PHPIZE_DEPS

RUN docker-php-ext-install pdo pdo_mysql opcache

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www
COPY . .

RUN composer install --no-dev --optimize-autoloader

EXPOSE 8000
CMD ["php", "artisan", "octane:start", "--server=swoole", "--host=0.0.0.0", "--port=8000"]

docker-compose.yml

version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8000:8000"
    environment:
      APP_ENV: production
      APP_KEY: "${APP_KEY}"
      DB_HOST: db
      REDIS_HOST: redis
      OCTANE_SERVER: swoole
      OCTANE_WORKERS: 4
      OCTANE_MAX_REQUESTS: 500
    depends_on:
      - db
      - redis
    restart: unless-stopped

  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: secret
    volumes:
      - db_data:/var/lib/mysql

  redis:
    image: redis:7-alpine
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis_data:/data

volumes:
  db_data:
  redis_data:

Конфигурация config/octane.php (ключевые параметры)

return [
    'server' => env('OCTANE_SERVER', 'swoole'),
    'workers' => env('OCTANE_WORKERS', 4),
    'task_workers' => env('OCTANE_TASK_WORKERS', 2),
    'max_requests' => env('OCTANE_MAX_REQUESTS', 500),
    'listeners' => [
        // Сброс синглтонов между запросами
        RequestReceived::class => [
            EnsureUploadedFilesAreValid::class,
        ],
    ],
    'warm' => [
        // Классы, которые нужно прогреть при старте
        ...Octane::defaultServicesToWarm(),
    ],
];

Параметр max_requests задаёт количество запросов, после которых воркер перезапускается. Это страховка от медленных утечек памяти: даже если вы допустили небольшую утечку, воркер не будет жить вечно и накапливать проблему.

Типичные ловушки: утечки памяти, статическое состояние, синглтоны

Переход на Octane — это не просто смена команды запуска. Резидентный процесс меняет правила игры, и то, что в PHP-FPM было незаметным, в Octane становится критической ошибкой.

Статические свойства

Статические свойства PHP живут весь срок жизни процесса. Если где-то в коде есть static $cache = [], и в него пишут данные без сброса, между запросами будут «протекать» данные одного пользователя к другому.

// ПЛОХО — данные аккумулируются между запросами
class UserRepository
{
    private static array $cache = [];

    public static function find(int $id): User
    {
        return static::$cache[$id] ??= User::find($id);
    }
}

// ХОРОШО — использовать Redis или request-scoped кэш
public function find(int $id): User
{
    return Cache::store('redis')->remember("user:{$id}", 60, fn() => User::find($id));
}

Синглтоны в сервис-контейнере

Если вы зарегистрировали класс как singleton через app()->singleton() и этот класс хранит состояние, оно сохранится между запросами. Octane предоставляет хук RequestReceived, где можно сбрасывать такие объекты:

// В AppServiceProvider
public function boot(): void
{
    Octane::listen(RequestReceived::class, function () {
        app()->forgetInstance(MyStatefulService::class);
    });
}

Соединения с базой данных

Laravel переиспользует соединения с БД между запросами. Это хорошо для производительности, но если транзакция не была закоммичена или соединение зависло, следующий запрос получит «грязное» состояние. Используйте DB::reconnect() в обработчиках ошибок и следите за mysql_wait_timeout.

Интеграция с Redis для кэширования сессий и очередей

Redis в связке с Laravel Octane — это стандартная конфигурация для production. Рассмотрим три ключевых сценария.

Сессии

# .env
SESSION_DRIVER=redis
SESSION_LIFETIME=120
REDIS_HOST=redis
REDIS_PORT=6379

Храните сессии в Redis, а не в файлах — при нескольких воркерах файловые сессии не синхронизируются между процессами.

Кэш

CACHE_DRIVER=redis

Octane прогревает кэш-драйвер при старте воркера. Redis-соединение переиспользуется между запросами, что даёт дополнительный выигрыш по latency.

Очереди

Запускайте queue worker как отдельный контейнер — не в том же процессе, что Octane. Это изолирует воркеры очередей от HTTP-трафика и упрощает масштабирование.

  queue:
    build:
      context: .
      dockerfile: Dockerfile
    command: php artisan queue:work redis --sleep=3 --tries=3
    depends_on:
      - redis
    restart: unless-stopped

Бенчмарки: RPS до и после Octane на реальном приложении

Приведём результаты нагрузочного тестирования реального Laravel-приложения (CRUD API, авторизация JWT, запросы к MySQL, кэш Redis). Тестирование проводилось инструментом wrk на сервере 4 vCPU / 8 GB RAM.

  • PHP-FPM (Nginx + PHP 8.3): ~420 RPS, среднее время ответа 240 мс при 100 конкурентных подключениях.
  • Laravel Octane + Swoole (4 воркера): ~1850 RPS, среднее время ответа 54 мс при тех же условиях.
  • Laravel Octane + RoadRunner (4 воркера): ~1540 RPS, среднее время ответа 65 мс.

Прирост производительности в 4–4,5 раза на Swoole и в 3,5 раза на RoadRunner — типичный результат для приложений с умеренной бизнес-логикой. На приложениях с тяжёлыми SQL-запросами разница меньше, поскольку узким местом становится база данных, а не бутстрап PHP.

Важно: бенчмарки без нагрузки на реальный сценарий (аутентификация, работа с БД, кэш) дают завышенные цифры. Всегда тестируйте на репрезентативном трафике.

Рекомендации по деплою в продакшен

Переход на Octane в production требует нескольких дополнительных шагов по сравнению со стандартным Laravel-деплоем.

Nginx как reverse-proxy

Не выставляйте Swoole-сервер напрямую в интернет. Nginx должен принимать TLS, обслуживать статику и проксировать динамику на Octane:

server {
    listen 443 ssl;
    server_name example.com;

    location /storage {
        root /var/www/public;
    }

    location / {
        proxy_pass http://app:8000;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Graceful reload

При деплое не убивайте воркеры жёстко. Octane поддерживает php artisan octane:reload, который отправляет сигнал воркерам завершить текущий запрос и перезапуститься с новым кодом. В Docker-окружении это можно реализовать через health-check и rolling update в Kubernetes или Docker Swarm.

Мониторинг

  • Следите за потреблением памяти каждого воркера — резкий рост указывает на утечку.
  • Экспортируйте метрики через Laravel Telescope или Prometheus + php-fpm-exporter (для Swoole есть отдельный экспортер).
  • Настройте алерты на 95-й перцентиль latency и на количество перезапусков воркеров.

Чеклист перед выходом в production

  1. Проверьте все синглтоны на наличие состояния, которое не сбрасывается между запросами.
  2. Переведите сессии, кэш и очереди на Redis.
  3. Установите max_requests в разумное значение (300–1000 в зависимости от приложения).
  4. Настройте Nginx как reverse-proxy с поддержкой keep-alive.
  5. Проведите нагрузочное тестирование на staging с реальным профилем трафика.
  6. Настройте graceful reload в пайплайне деплоя.
  7. Добавьте мониторинг памяти и latency.

Заключение

Laravel Octane со Swoole — это не серебряная пуля, но один из самых доступных способов кратно увеличить производительность PHP-приложения без смены стека и переписывания бизнес-логики. В 2026 году инструмент достаточно зрелый, хорошо документированный и проверенный в production у крупных команд.

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

Технологии

Теги

Руслан Исмаилов

Senior Web / Backend разработчик. Senior web/backend разработчик с 9-летним опытом. Стек: PHP, Laravel, PostgreSQL, Redis, Docker, Kubernetes, REST, микросервисы, CI/CD. Подробнее обо мне →