• Блог
  • Ускоряем отдачу страниц

В этой статье я покажу как сделать так, чтобы страница сайта отдавалась со скоростью статической. Всё что нужно — это использовать кэш. Реализаций может быть множество. Я предлагаю через кэширование запросов. Выглядит это так:

  • Поступил запрос.
  • В событии OnHandleRequest проверяем, есть ли для него сохранённая в кэше страница. Если есть, выдаём её пользователю и завершаем работу. Если нет, то идёт полноценная обработка.
  • В событии OnWebPageComplete сохраняем готовую страницу в кэше.

Т.е. запрос пришёл и сразу вернулся результат, значительно сократив путь. Как в магазине ИКЕА — есть специальные проходы (перемычки), позволяющие срезать путь и не проходить весь магазин целиком.

Но как вы уже, наверное, поняли, это полностью готовая страница. Все теги MODX (и кэшированные и некэшированные) распарсены. Поэтому этот приём не подойдёт для страниц с динамическим формированием контента с помощью некэшированных сниппетов и чанков. А также, если у вас логика завязана на плагины. В данном случае очередь до них не дойдёт.

Соответственно нужно исключить и страницы с личной информацией типа страниц с профилем или содержащие id и имя пользователя, токен, почту и т.д. Страница закэширует данные первого пользователя и остальные пользователи получат их вместо своих данных.

Тогда для чего же всё это нужно? А вот для всяких лендингов, справочников, документации, галерей и т.п. Т.е. страниц с минимальной логикой. В принципе, при желании, можно внедрить любые данные. Например, так

$content = str_replace("</body>", "<script> var userID=".user_id().";</script>\n</body>", $content);

Но давайте обо всём по порядку.

Наша задача как можно раньше вернуть ответ пользователю. Поэтому будем использовать событие OnHandleRequest. Но как вы знаете, когда срабатывает это событие, ресурс ещё не определён. В нашем распоряжении есть только URL запроса. Вот к нему мы и привяжемся. Будем использовать его в качестве ключа для кэша. При первом обращении кэш у нас пустой. Поэтому запрос пройдёт полный путь. И в конце полученная страница сохранится в кэш. Все последующие запросы получат страницу уже из кэша. Дальше события OnHandleRequest они не пройдут.

Итак, поехали. Создаём плагин. Для примера будем кэшировать все страницы с адресом site.ru/documentation, site.ru/documentation/page1 и т.п.

switch ($modx->event->name) {
    case 'OnHandleRequest':
        if (request()->segment(1) == 'documentation') {
            // Формируем ключ. Заменяем слеши и тире в URL на подчёркивание
            $key = str_replace(['/','-'],'_',request()->path());
            if ($content = cache($key,'requests')) {
                // Здесь можно поработать с контентом - что-то добавить или заменить.
                echo $content;
                while (ob_get_level() && @ob_end_flush()) {}
                flush();
                exit;
            }
        }	
		break;
    case 'OnWebPageComplete':
        $content = resource(true)->_output;
        $key = str_replace(['/','-'],'_',request()->path());
        if (!cache($key, 'requests') && request()->match('documentation*')) {
            // Сохраняем кэш в отдельный раздел 
            cache([$key => $content], 'requests');
        }
        break;
}

В папке core/cache/requests будут появляться файлы типа documentation.cache.php, documentation_page1.cache.php, в которых будут лежать готовые страницы.

Если на странице есть какие-то динамические данные типа статистики просмотра и т.п., то можно сохранять кэш на 1 минуту, чтобы почаще обновлять эти данные.

Уверен, многим было бы интересно увидеть результат данной оптимизации. Тестируем страницу документации.

Показатели Обычный режим MODX С использование кэша
Запросы 43 2
Время обработки запросов 0.0604 s 0.0004 s
Время обработки PHP 0.0890 s 0.0065 s
Общее время 0.1494 s 0.0069 s
Память 4 096 Kb 2 048 Kb

Результат говорит сам за себя. Я думаю, многие могут найти у себя страницы, к которым можно применить данную оптимизацию. Кстати, она будет работать и для API запросов — получили запрос, проверили, есть ли для него что в кэше, есть — отдаём, нет — работаем.

Это всё, что я хотел сказать.

П.С. Кстати, хорошая идея для компонента кэширования запросов. Дарю :) А ресурсы для кэширования можно определять через соответствующую ТВшку.

7   5498

Комментарии ()

  1. Сергей Шлоков 02 марта 2018 # +1
    Небольшую динамику можно добавить обернув код в кастомный тег
    # HTML
    <calc>
    [[!SomeCalculations]]
    </calc>
    
    При сохранении в кэш регуляркой вырезать содержимое этого тега и вставить в него [[!SomeCalculations]].
    А перед выводом из кэша распарсить
    ...
    $content = parse($content, [], true);
    echo $content;
    ...
    
    И таких тегов можно сделать сколько угодно — snippet_mysnippet1, chunk_statistics и т.д.

    Также не забываем, что можно запустить любой плагин через $modx->invokeEvent(). Например, тот же Tickets ведёт статистику просмотров в событии OnWebPageComplete. Поэтому вызываем его перед выдачей ответа:
    ...
    $modx->invokeEvent('OnWebPageComplete');
    exit;
    
    Пример чисто для понимания возможностей.
    1. shock 04 марта 2018 # +2
      Спасибо!
      Очередной полезный функционал.
      «О сколько нам открытий чудных....»:)
      1. Сергей Шлоков 04 марта 2018 # 0
        На здоровье!:E
      2. Евгений 19 сентября 2022 # 0
        Хороший пример, еще есть готовый кэширующий плагин: github modxExtraCachePlugin
        тут есть прогрев кэша и хранение сессий, т.к. много компонентов их использует.

        Вы должны авторизоваться, чтобы оставлять комментарии.

        Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.