Как известно, nginx умеет кешировать ответ сервера, и выдавать его по запросу вместо обращения к бэкенду, экономя тем самым ресурсы сервера. Скорость отдачи таких закешированных страниц иногда поражает, ради таких скоростей иногда не жалко переносить на javascript многие функции сайта только для того, чтобы иметь возможность закешировать ещё 1 страницу целиком (Например, вынести отрисовку плашки с авторизацией юзера на js, чтобы иметь возможность кешировать страницу, которая идентична для всех пользователей, за исключением этой самой плашки).

 Я много раз использовал возможность кэширование nginxом страниц, и натыкался на пару неудобных для себя вещей:

  • Можно легко закешировать вообще все страницы, но для динамических сайтов или для сайтов с авторизацией нужно ли это?
  • Можно закешировать отдельно несколько url, вида /album/*, но не переписывать же конфиг nginx каждый раз при появлении новых разделов сайта?



Если проект в дополнение ко всему выкладывается админами, раскладывается по нескольким серверам, обрабатывается несколькими nginx — переписывание конфигов становится нетривиальной задачей.

Для себя эту задачу я решил при помощи заголовка X-Accel-Expires и нескольких строк в конфигурации nginx.

Задача


Для своего решения по кешированию страниц я сформировал следующие требования:
1. Управлять включением или выключением кеширования каждой страницы из php.
2. Управлять кешированием по полному url.
3. Управлять временем кеша каждой отдельной страницы или раздела сайта.
4. Иметь возможность поменять время кеширование и все настройки кеша в любой момент без перезаписывания конфигурации nginx.

Решение


В моем случае используется fastcgi (php для отдачи контента, nginx для отдачи статики)

Решим задачу в несколько шагов:
1. Зададим в конфигурации nginx новую зону для кеширования. Строка прописывается перед разделом «server» с конфигурацией нашего сайта, например.

fastcgi_cache_path /tmp/mycache levels=2 keys_zone=mycachename:5m inactive=2m max_size=1500m;



2. В разделе server задаем кеширование страниц следующим образом:

location / {
                        root $PROJECT_ROOT/data/ ;
                        fastcgi_cache mycachename;
                        fastcgi_cache_valid 200 301 302 304 30m;
                        fastcgi_cache_key "$request_method|$http_if_modified_since|$http_if_none_match|$host|$request_uri";
                        fastcgi_cache_use_stale error timeout invalid_header;
                        fastcgi_pass_header "X-Accel-Expires";
                        fastcgi_pass   127.0.0.1:9900;
                        fastcgi_index  index.php;
                }



Здесь обращаем внимание на следующие параметры:
fastcgi_cache — имя заданной в пункте 1 зоны
fastcgi_cache_valid — для каких кодов ответа включаем кеширование. Последний параметр — время кеширования — фактически будет максимальным временем кеша для страницы. Т.е. мы сможем управлять временем кеша от 1 секунды до 30 минут.
fastcgi_cache_key — чтобы не закешировать действительно разные запросы в одном файле
fastcgi_pass_header «X-Accel-Expires» — необязательно, но полезно при отладке — смотреть, на сколько в итоге мы закешировали страницу.

3. В самом php дописываем следующие вещи:
Ставим заголовок по умолчанию:

<?php
header("X-Accel-Expires: 0");


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


function flushResponse() {
        if (self::$cache_time) {
            // nginx cache
            header("X-Accel-Expires: " . (self::$cache_time), true);
        }
        ob_flush();
    }


Здесь мы проверяем, можно ли кешировать страницу (в моем случае по некоторой логике или из конфигурации каждой страницы сайта мы получаем время кеша страницы в секундах) и выставляем заголовок X-Accel-Expires еще 1 раз, перезаписывая выставленный ранее со значением 0.

Что происходит?


Таким образом, nginx при обработке запроса делает следующее:
1. Проверяет, не лежит ли запрашиваемая страница в кеше (имя файла кеша определяется по параметру fastcgi_cache_key)
2. Если лежит — отдает контент без обращения к php и рвет коннект.
3. Выполняет php скрипт, получает в ответ заголовок X-Accel-Expires. Если он равен 0 — ничего не кеширует, иначе кеширует на заданное в заголовке количество секунд. Максимальное время в нашем случае — 30 минут, определяется последним аргументов параметра fastcgi_cache_valid.

Заключение


Таким образом можно удобно управлять мощным кешированием nginx, не переписывая конфигурацию сервера. Именно «переписывание конфигурации» и прописывание условий кеширования в конфигах отталкивало меня от использования этой технологии долгое время.

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

Вверх