20 ноября 2010 г. FastCGI Nginx SSI Кеш

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

Устал я от того что мой блог долго грузится, но менять движок с 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" на что-то своё, чтобы скрипткидесы не баловались.

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

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

Комментарии

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

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

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

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

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

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

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

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

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

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

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

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

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

fastcgi_temp_path и fastcgi_cache_path

в чем отличие ?

все заметил описано в самом вверху =_

Александр 17 мая 2012 г. 14:14

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

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

Александр 17 мая 2012 г. 16:53

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

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

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

Виталий 7 июня 2013 г. 15:12

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

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

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

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

Виталий 8 июня 2013 г. 9:03

Может вы мне поможете
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 но не из кешированых.

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

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

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

Виталий 8 июня 2013 г. 10:04

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

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

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

Виталий 8 июня 2013 г. 11:17

Для того чтобы избавиться от дублирующихся запросов и кэшировать то что уже проверялось на два дня или больше.
Получилось в 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 на два дня и отдавать кеш редирект со страшной скоростью!

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

Виталий 8 июня 2013 г. 11:38

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

ЯП - язык программирования

А как отключить кеширования в админке?

Видимо сделать отдельный локейшен для админки и почитать http://nginx.org/ru/docs/http/ngx_http_fastcgi_module.html#fastcgi_cache :

location /admin/ {
    fastcgi_cache off;
}

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

не понял про обновление 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 ?
Извините если туплю. вышлите полный конфиг на почту

Оставьте свой комментарий

Markdown