Перейти к содержанию

Включить кэш маршрутов

Файл маршрутов выполняется при каждом запуске приложения. И каждый listen() не просто запоминает строчку: он разбирает шаблон, компилирует его в регулярное выражение и подгружает класс слушателя, чтобы убедиться, что тот существует. Для двадцати маршрутов это незаметно. Для двухсот — это двести файлов, которые автозагрузчик открывает на каждом запросе, даже если ни одного события так и не случится.

Кэш убирает эту работу. Таблица маршрутов собирается один раз и сохраняется в PHP-файл. Дальше роутер берёт её оттуда, а файл маршрутов не выполняется вовсе.

Как включить

Два шага. Путь к файлу кэша — в конфиг фабрики, маршруты — через loadRoutes():

use Selyusize\EventsRouter\Event;
use Selyusize\EventsRouter\EventRouter;
use Selyusize\EventsRouter\EventRouterFactory;

// Файл маршрутов, как обычно, — функция, которая получает роутер
$routes = static function (EventRouter $events): void {
    echo '    выполняется файл маршрутов', PHP_EOL;

    $events->setPrefix('shop');
    $events->listen('order.{order_id}.paid', MarkOrderPaid::class);
};

foreach (['первый запуск', 'второй запуск'] as $run) {
    echo $run, ':', PHP_EOL;

    $events = EventRouterFactory::create(config: ['route_cache_file' => $cacheFile]);
    $events->loadRoutes($routes);

    $events->dispatch(new Event('shop.order.42.paid'));
}
первый запуск:
    выполняется файл маршрутов
    MarkOrderPaid: заказ 42
второй запуск:
    MarkOrderPaid: заказ 42

При первом запуске файла кэша нет: роутер выполняет файл маршрутов и записывает таблицу. При втором файл маршрутов уже не нужен — всё берётся из кэша.

С кэшем роутер с 200 слушателями собирается за 2,4 мкс вместо 643 мкс, а запрос PHP-FPM, который собирает роутер и рассылает три события, занимает около 21 мкс — в 1,8 раза быстрее, чем symfony/event-dispatcher, и в 60 раз быстрее, чем без кэша. Классы слушателей из кэша не загружаются вообще — только когда до слушателя дойдёт событие. Подробности и как мерили — в Производительности.

Где включать

Только в проде. На разработке маршруты меняются постоянно, а кэш сам не обновляется: забытый файл кэша — это маршрут, который «точно добавлен», но не срабатывает.

$events = EventRouterFactory::create($container, [
    'route_cache_file' => $isProd ? __DIR__ . '/var/cache/events-routes.php' : null,
]);
$events->loadRoutes(require __DIR__ . '/events-routes.php');

При деплое удаляйте файл кэша. Новый соберётся на первом запросе. Если в проде OPcache не проверяет время изменения файлов (opcache.validate_timestamps=0), сбросьте и его — как вы и так делаете после деплоя.

Правила, которые появляются вместе с кэшем

Кэш работает по принципу «всё или ничего». Чтобы прод и разработка вели себя одинаково, роутер следит за тремя вещами и сразу бросает InvalidRoute, а не ждёт, пока расхождение всплывёт в проде:

  • Маршруты объявляются только внутри loadRoutes(). listen(), group() и setPrefix() снаружи — ошибка. Из кэша такой маршрут бы просто пропал.
  • loadRoutes() вызывается один раз. Все маршруты — из одного файла.
  • Middleware — только именем класса. Объект в PHP-файл не записать: ->add(IdempotencyGuard::class) вместо ->add(new IdempotencyGuard(...)). Зависимости такому middleware подставит контейнер.

Middleware роутера ($events->add()), добавленные внутри файла маршрутов, тоже попадают в кэш. Добавленные снаружи, до или после loadRoutes(), работают как обычно и в кэш не пишутся.

Если файл кэша испорчен

Повреждённый файл или файл от старой версии библиотеки роутер не использует: он соберёт маршруты заново и перезапишет кэш. Если записать не удалось — нет прав, нет места, — маршруты всё равно работают, а PHP получает предупреждение E_USER_WARNING.