← Все записи

Nginx. Кеширование с помощью fastcgi_cache

Логотип Nginx

Устал я от того что мой блог долго грузится, но менять движок с Wordpress на что-либо другое пока нет времени, а переезд на новый более мощный сервер всё время затягивается и решил прикрутить кеширование на стороне Nginx. Так как блог работает на fastcgi, то буду использовать fastcgi_cache, если вы используете связку Nginx+Apache, то вам следует использовать proxy_cache, как я понял там отличий особых нет.

Некоторые блоки мне всетаки надо оставить вне кеша, поэтому буду использовать ssi.

Настройка

У меня много хостов на одном сервере, но мне надо настроить кеш только для одного хоста. Определим кеш для моего блога в nginx.conf, секция http:


fastcgi_cache_path /var/cache/nginx/adw0rd
    levels=1:2
    keys_zone=adw0rd:16m
    max_size=256m
    inactive=1d;

Где,

fastcgi_cache_path - путь до каталога кеша, создайте его заблаговременно. levels= - разделять файлы кеша на сабкаталоги заданного уровня, то есть “1:2” - это так:


/0/ab/b7f54b2df7773722d382f4809d650ab0

либо так, если не указывать LEVEL, то есть levels оставить пустым (levels=):


/b7f54b2df7773722d382f4809d650ab0

max_size - максимальный размер кеша. incative - время жизни кеша.

Настройка виртуального хоста

Я разделяю конфиги на основной (nginx.conf) и на хосты (adw0rd.conf, pyha.conf, etc). Сейчас мы разберемся с adw0rd.conf:


server {
    # Порт который слушаем
    listen 80;
    # Имя хоста
    server_name adw0rd.ru;
    # Путь до htdocs (document_root)
    root /path/to/www;

    # включаем использование ssi
    ssi on;

    location / {
        # Если такой файл не существует физически, то переходить на крайний аргумент, то есть @wordpress
        try_files $uri @wordpress;
    }

    # обработчик для wordpress
    location @wordpress {

        # Проксируем на php-fpm
        fastcgi_pass php-fpm;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/index.php;
        fastcgi_param SCRIPT_NAME /index.php;

        # Настройки кеширования
        fastcgi_temp_path /tmp/nginx 1 2; # Каталог для хранение временных файлов
        fastcgi_cache adw0rd; # Имя зоны кеша
        fastcgi_cache_key "$request_method|$host|$request_uri"; # Из этих данных строится хеш
        fastcgi_hide_header "Set-Cookie"; # Не передавать клиенту заголовки кук
        fastcgi_cache_min_uses 1; # Кол-во запросов, после которых ответ будет закеширован
        fastcgi_cache_valid 10m; # Время жизни кеша
        fastcgi_cache_use_stale error timeout invalid_header http_500; # Случаи при которых будет использоватся старый кеш

    }

    # обработчик для динамических данных на странице, ниже я покажу как юзать это
    location ~ ^/ssi/ {
        fastcgi_pass php-fpm;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;        
    }

    # Обработчик *.php файлов, например "wp-comments-post.php"
    location ~ \.php$ {

        # если файл физически не существует, то переходим в @wordpress
        try_files $uri @wordpress;

        # иначе обрабатываем запрос
        fastcgi_pass php-fpm;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Обо всех директивах можете прочитать в оф. доке Директивы модуля ngx_http_fastcgi_module.

Тестирование

Перезагружаем Nginx:


nginx -s reload

Для тестирования кеша я встроил в шаблон html-комментарий, который формирую так:


<!-- cache: <?php echo time() ?> -->

И теперь вижу в сорце странице закешировалась страница или нет.

Вставка динамических блоков при помощи SSI

Отредактируйте шаблон своей страницы и вставьте нечто подобное:


<!-- uncached_data: <!--# include virtual="/ssi/uncached.php" wait="no" --> -->

Теперь приведите файл “/ssi/uncached.php” к следующему виду:


<?php
echo time();

Так как у нас есть обработчик действующий для /ssi/* и он не отдает кешированные данные, то у нас будет подгружатся динамика, все просто! :)

Дополнение

Раздел добавлен 8 августа 2011 г., чтобы дополнить эту статью недостоющими материалами. В основном причиной была статья Прозрачное кэширование в nginx для всех и каждого, спасибо авторам.

Директива fastcgi_ignore_headers для игнорирования инструкци от FastCGI-сервера

Директива fastcgi_ignore_headers запрещает обработку некоторых строк заголовка из ответа FastCGI-сервера.
В директиве можно указать строки “X-Accel-Redirect”, “X-Accel-Expires”, “Expires” и “Cache-Control”.

fastcgi_ignore_headers Expires Cache-Control;

То есть указываем Nginx кэшировать страницы в любом случае, независимо от заголовков кэширования выставленных FastCGI-сервером.

Директивы fastcgi_cache_bypass и fastcgi_no_cache

Директива задаёт условия, при которых ответ не будет браться из кэша.
Если значение хотя бы одной из строк переменных не пустое и не равно “0”, то ответ не берётся из кэша:

fastcgi_cache_bypass $cookie_session $cookie_nocache;

Директива задаёт условия, при которых ответ не будет сохраняться в кэш.
Если значение хотя бы одной из строк переменных не пустое и не равно “0”, то ответ не будет сохранён:

fastcgi_no_cache $cookie_session $cookie_nocache;

Управление кеширование через заголовок X-Accel-Expires

Будем управлять кеширование из PHP:

<?php
header("X-Accel-Expires: $seconds"); // ставим на $seconds секунд
header("X-Accel-Expires: 0"); // или отключаем кеширование для определенной страницы

Принудительное обновление кэша

Обновим наш bypass, добавим еще одну переменную для заголовка “X-Update”:

fastcgi_cache_bypass $cookie_session $http_x_update;

Ну а теперь обновим кеш:

curl -s -o /dev/null -H "X-Update: 1" example.com

Как и рекомендуют авторы статьи необходимо изменить “$http_x_update” и “X-Update” на что-то своё, чтобы скрипткидесы не баловались.

Полезные материалы

Вот небольшой список статей, который вам могут пригодится:

Комментарии 28

Архив обсуждения. Новые комментарии закрыты.

mario

круто! :)
объясни плз различие: inactive=1d; между fastcgi_cache_valid 10m;
И правильно ли я понял, если у стоит fastcgi_cache_valid 10m; то новые комментарии будут появляться через 10минут? Или же за счет того что try_files $uri @wordpress; у нас будет генериться новый кеш при создании коммента?

mario

а вот и нет… кеш заново не генерируется, но страница открывается “налету”! :)

adw0rd

inactive=1d;
Кеш который старше этого времени просто удаляется, типа чистки

fastcgi_cache_valid 10m;
А тут, при запросе пользователя, если кеш устарел, то получаются данные и помещаются в кеш

mario

Спасибо! :) Ты как всегда крут! Слушай, drupal на таком конфиге должен летать :)

Fighter

А если требуется вручную обновить кэш, как это сделать?

Fighter

И еще вопрос, в этом примере я так понимаю весь кэш генерирует только файл /index.php ?

adw0rd

А если требуется вручную обновить кэш, как это сделать?

Очистить каталог и ребутнуть nginx наверное

adw0rd

И еще вопрос, в этом примере я так понимаю весь кэш генерирует только файл /index.php ?

кеш “генерирует” nginx, а index.php входная точка для роутов

Fighter

Насчет обновления кэша есть purge

>index.php входная точка для роутов
ну то есть этот скрипт как я понимаю и отдает данные, которые потом кэшируются nginx’ом

Александр

На одном своем проекте заметил, что кеш не создается, если брать страницы через wget или apache jmeter. Но если через браузер открывать, то все нормально. У меня параметр proxy_cache_key “$request_method|$host|$request_uri”;
А вы не сталкивались с подобным?

adw0rd

Нет, не сталкивался, возможно дело в заголовках

Александр

Дело похоже в session_start(), вызов которой автоматически приводит к отдаче сервером заголовков expires/cache-control/pragma, т.к. без старта сессии при запросе через wget nginx кеширует.

Но в конфиге у меня прописано
proxy_hide_header “Set-Cookie” ;
proxy_ignore_headers “Cache-Control” “Expires”;

Осталось разобраться почему при старте сессии в браузере nginx из кеша отдает, а через wget кеш не работает.

Виталий

keys_zone=adw0rd:120m
А ещё если здесь поставить побольше времени на пример 120m или ещё больше то жаде если php и база данных перестанут работать, Nginx по прежнему будет 2 часа показывать статику!
После того как пройдёт 120 минут, а php и база данных так и не заработала бдет ошибка базы данных или 502.

adw0rd

keys_zone=имя:размер
Кроме того, все активные ключи и информация о данных хранятся в зоне разделяемой памяти, имя и размер которой задаются параметром keys_zone. Если к данным кэша не обращаются в течение времени, заданного параметром inactive, то данные удаляются, независимо от их свежести. По умолчанию inactive равен 10 минутам.

Читайте об этом тут

Вообщем вам надо использовать inactive.
И прочитайте про fastcgi_cache_use_stale

Виталий

Может вы мне поможете
limit_req_zone $server_name zone=one:10m rate=30r/m;
нужно что бы лимит срабатывал не на всех php запросах, а только на php запросах которые ещё не закэшированы.
То есть если новые запросы - ставим их в очередь 1 запрос за 2 секунды на обработку в php-fpm и кешируем, а если запрос уже закэширован тогда отдаём сразу без лимита.
limit_req zone=one burst=30;

Или что-то нужно в PHP-FPM крутить чтобы нельзя было одновременно запускать несколько php сразу, не более одного php запроса в две секунды? Нужно очередь создать из php но не из кешированых.

adw0rd

Я особо не понял зачем вам это надо, но кешируются не реквесты, а респонсы, вам надо продумать ключ “fastcgi_cache_key” и тогда будут кешировать только то что надо.

чтобы нельзя было одновременно запускать несколько php сразу

Какую проблему вы решаете? Снизить нагрузку?
Я с php уже не работал года 3, так что лучше спросить актуальные данные на каком-нибудь форуме пхпшников

Виталий

Настраиваю мониторинг удалённых видео VK. PHP подключается к другому серверу, нужно ограничить число подключений в секунду - то есть выстраивать в очередь если их много, а потом кешировать.

adw0rd

То есть у вас есть приложение, которое ходит на сервера VK и проверяет наличие видео? Вам надо ограничить кол-во запросов к серверам VK и кешировать ответы на запросы?

Тогда не пойму причем тут fastcgi_cache

Виталий

Для того чтобы избавиться от дублирующихся запросов и кэшировать то что уже проверялось на два дня или больше.
Получилось в IIS, оказывается нужно зайти в Парметры FastCGI и поставить максимальное число экземпляров 1, а в php файле добавить sleep(2);

// текущее время
echo date(‘h:i:s’) . “\n”;
// ожидание в течении 10 секунд
sleep(10);
// завершение ожидания
echo date(‘h:i:s’) . “\n”;

Осталось только в php-fpm.conf настроить pm = static и разрешить запускать только 1 процесс php-fpm, а потом это закэшировать в Nginx на два дня и отдавать кеш редирект со страшной скоростью!

adw0rd

Вместо того, чтобы написать логику на ЯП, для получения и кеширования респонсов, вы колдуете с настройками Nginx/php-fpm. Все верно?

Виталий

Верно, я разбираюсь только в Nginx/php-fpm, а ЯП что это? Ним можно проверить размер?

Виталий

У меня на сайте всё кешируется без php сессии, а когда не надо кэшировать специальная картинка ставит Cookies которое отключает всё кэширование Nginx для конкретного чела и включается php сессия. Все роботы и программы для автоматических регистраций ходят по кэшированной версии сайта - они не загружают графику кроме капчи, а люди которые желают зарегистрироваться или войти на сайт видят специальную картинку c Cookies и динамические страницы.
Это позволяет снизить нагрузку в разы!

Sergey

не понял про обновление fastcgi кеша
Если в конфигурации прописал fastcgi_cache_bypass $http_cache_update;
то теперь чтоб обновить индексную страницу сайта необходимо выполнить
curl -s -o /dev/null -H “CACHE-UPDATE: 1” http://example.com/
или где определяется переменная $http_cache_update или как в прошлой конфигурации $http_x_update.
Где описывается значение X-Update ?
Извините если туплю. вышлите полный конфиг на почту