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

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

  • Поступил запрос.
  • В событии 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 запросов — получили запрос, проверили, есть ли для него что в кэше, есть — отдаём, нет — работаем.

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

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

    01 марта 2018, 17:20   914     3

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

    1. Сергей Шлоков 02 марта 2018, 08:40 # +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, 21:18 # +2
        Спасибо!
        Очередной полезный функционал.
        «О сколько нам открытий чудных....» :)
        1. Сергей Шлоков 04 марта 2018, 22:31 # 0
          На здоровье! :E

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

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