Кэширование в Nginx






В статье посвящённой обратному проксированию в Nginx мы с вами не коснулись одной важной темы — кэширования отдаваемого upstream-серверами контента. Когда клиент выполняет запрос, например, к PHP-сценарию, его обработкой занимается upstream-сервер. Работа сценария часто связана с вызовом других сценариев, запросам к базам данных, выполнении довольно тяжёлых в плане потребления ресурсов сервера операций. И очень часто вся эта «каша» заваривается для того, чтобы просто отдать клиенту сформированную страницу; т. е. следующий запрос к этому же сценарию с теми же параметрами запустит ещё один процесс обработки с точно такими же условиями и результатами. А что будет с сервером, когда таких запросов будет несколько десятков в секунду? Несколько сотен? На эти вопросы и на то, как с этим жить, имея в распоряжении Nginx, попытаемся ответить в этой статье.


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

Исходные данные

В качестве PHP-сценария для проведения тестов использовался небольшой скрипт, выбирающий все строки из таблицы БД MySQL и выводящий результаты при помощи var_dump ():

Структура таблицы test1 БД следующая:

Таблица содержит 10000 записей, где в каждом поле name записан MD5-хеш случайного числа в диапазоне от 0 до time (). URL сценария, работающего под Apache — http://test.ashep:80/test.php. Давайте посмотрим при помощи siege как сервер будет обрабатывать запросы к этому сценарию.

Прямые запросы к Apache

Эмулируем 10 серий по 10 одновременных запросов:

Сервер успешно обработал все запросы, однако время на обработку каждого составило в среднем 10,3 секунд.

Проксирование через Nginx

Теперь создадим реверс-прокси сервер в Nginx так, как мы это делали в предыдущей статье, в качестве upstream-сервера будем использовать наш Apache по http://test.ashep:80.

Повторим тест, на этот раз уже через Nginx:

Результаты примерно те же. Логично, поскольку Nginx всего лишь транслирует запросы к upstream-серверу, ничего при этом не кэшируя.

Кэширующее проксирование через Nginx

И теперь самое интересное. Прежде, чем можно будет оперировать кэшированием в настройках серверов Nginx, сперва нужно определить место для хранения и параметры кэша Nginx. Кэшей в Nginx можно определить больше одного, каждый с нужными вам параметрами и затем использовать их в зависимости от ситуации в том или ином месте конфигурации сервера.

Определять кэш можно лишь в контексте секции http, которая, например, в моём Debian описана в /etc/nginx/nginx.conf. Для определения кэша используется опция proxy_cache_path, которая имеет следующий формат:

В квадратных скобках приведены необязательные параметры. Теперь обо всём по порядку:

  • path определяет место в файловой системе, где будет храниться кэш. Кэш в Nginx хранится в виде обычных файлов, каждый из которых хранит содержимое ответа на какой-то запрос. URL запроса хешируется в MD5 и полученная строка используется в качестве имени файла кэша, а также ключа, по которому Nginx будет отыскивать нужный фрагмент кэша;
  • при помощи levels можно определить количество уровней вложенности каталогов с файлами кэша. Формат описания уровней может быть одним из: 'X', 'X:X' или 'X:X:X', что соответственно определяет один, два или три уровня вложенности каталогов. Каждая 'X' является единицей или двойкой и определяет количество символов в имени каталога соответствующего уровня;
  • значением параметра keys_zone определяется имя кэша (zone_name), на которое можно ссылаться из других участков конфигурации сервера, а также её размер (zone_size);
  • при помощи необязательного параметра inactive определяется время жизни объектов кэша, если к ним не было запросов. Т. е. если в течение указанного периода времени объект кэша ни разу не запрашивался — он подлежит удалению. Значение параметра inactive по умолчанию равно 10 минутам (10m);
  • значение необязательного параметра max_size определяет размер кэша, больше которого ему не будет позволено «раздуваться».

В ходе моих экспериментов, рассматриваемых в этой заметке, я использовал следующее значение опции proxy_cache:

Приведённой выше строкой создаётся двухуровневый кэш в каталоге /var/cache/nginx с именем 'default' и размером 100 мегабайт. Теперь определённый кэш default можно использовать в конфигурации серверов Nginx. Слегка дополненная конфигурация сервера, приводившаяся выше:

Обратите внимание на две новых опции. При помощи параметра proxy_cache мы указываем Nginx какой кэш необходимо использовать при кэшировании данных от upstream-сервера, в данном случае — это 'default', определённый ранее в /etc/nginx/nginx.conf. Опция proxy_cache_valid определяет время в течение которого не устаревшими будут считаться объекты кэша, полученные полученные в результате ответов upstream-сервера. Формат опции следующий:

Таким образом ответы upstream-сервера с кодами 200 и 302 будут кэшироваться на 10 минут, а ошибки 404 — в течение одной минуты.

Сохранив конфиг-файлы и перезапустив Nginx, попробуем провести нагрузочное тестирование с включённым кэшированием:

Среднее время ответа от сервера составило 0,73 секунды. Неплохо, правда? ;)




Кэширование в Nginx: 31 комментарий

  1. Отлично.

    Доступно и «понимабельно». Спасибо.

  2. Александр, посмотрите в сторону creative commons. Публиковали бы статьи с соответствующей лицензией — wikidadmin мог бы их взять и доработать.

    creativecommons.org — там информация, для wordpress есть плагин с таким же названием, в пару кликов добавляет информацию о лицензии для поисковиков и людей.

    1. Дмитрий, да я ж не возражаю, пусть берут кому нужно, дорабатывают, переписывают. Не жалко совершенно. Даже без лицензий ;)

  3. > Даже без лицензий ;)

    Нынче если брать без лицензий — к сожалению можно нарваться на проблемы. Ну вот вы — добрый. А вот кто-то еще — менее добрый и вот возьмешь у него статью, а потом по судам затаскают. Поэтому хорошо бы явно и прозрачно указывать правила игры. Ну, чтобы можно было понимать — что льзя, что нельзя.

    1. /bin/user, у меня нигде на сайте не написано о том, что права защищены. А раз права не защищены — пользуйтесь все на здоровье ;)

  4. Статья не плохая, но почему не освещены нюансы работы с cookies при такой схеме?

  5. > А раз права не защищены — пользуйтесь все на здоровье ;)

    К сожалению, законы о авторских правах немного другого мнения: по умолчанию все права — у автора. А если кто без лицензии взял — он, стало быть, пират. Ну вот такие вот законы протолкали господа демократы, увы.

  6. Подскажите как записать конструкцию

    location / {

    proxy_pass test.ashep:80;

    }

    если на сервере есть два сайта и по ip ничего не выдается, а nginx должен работать как кэширующий прокси на другом сервере.

    Вариант с указанием ip не работает.

    location / {

    proxy_pass xxx.xxx.xxx.xxx:80;

    }

    Планируется что DNS, настраиваемого домена, должен указывать на кэширующий сервер, т.е. прописать имя сайта я в proxy_pass не могу.

    Вариант с поддоменами выглядит некрасиво. Правильно ли я понимаю что единственный вариант правильно перенаправить запрос на нужный ip — это прописать в hosts кэширующего сервера домен и ip основного сервера, а в конфигурации написать proxy_pass domen.ru?

  7. А как быть если нужно кеш отдавать только просто посетителям, но если пользователь залогинировался, что бы динамику отдавать?

  8. Все сделал по мануалу- но не работает...

    пишет при рестарте

    «proxy_cache» zone «default» is unknown in /etc/nginx/nginx.conf:825

    configuration file /etc/nginx/nginx.conf test failed

    Куда копать?..

    Кстати, можно к Вам обратиться за настройкой VPS сервера? оптимизация очень нужна... Естественно, не бесплатно =)... а то толкового специалиста- очень тяжело найти...

    1. Прежде чем помочь вам определяться с направлением копания, хотелось бы увидеть конфиг полностью.

      Исходя из текста ошибки очевидно, что у вас не определена зона 'default' параметром proxy_cache_path.

      Лучше всего выложите конфиг на какой-нибудь pastebin и дайте ссылку, так легче будет диагностировать.

      Насчёт помощи в оптимизации VPS: увы, пока помочь не смогу.

      1. Прописал в nginx.conf вот так:

        http {

        ##

        # Basic Settings

        ##

        #кеш

        #proxy_cache_path /var/cache/nginx levels=2:2 keys_zone=default:1000m;

        sendfile on;

        tcp_nopush on;

        tcp_nodelay on;

        keepalive_timeout 65;

        types_hash_max_size 2048;

        # server_tokens off;

        и в секцию домена прописал вот так:

        location / {

        proxy_pass 1.1.1.1:8080;

        proxy_redirect site.ru:8080 /;

        proxy_cache_path /var/cache/nginx levels=2:2 keys_zone=default:100m;

        proxy_set_header Host $host;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header X-Real-IP $remote_addr;

  9. К своему стыду не знаю регэкспы. Подскажите, плиз, где можно почитать про них, очень желательно на русском? В основном интересуют для применения в nginx.

      1. вроде кое-что стало понятно.

        Вы не могли бы разъяснить такое выражение (определение userdir)

        location ~ ^/~(.+?)(/.*)?$

        ~ — начало регулярного выражения

        ^ — начало строки

        / — ?????

        ~ — тильда

        (.+?) — группа ????

        (/.*)? — группа ????

        $ — конец регулярного выражения

          1. (.+?) — группа: любой символ хотя бы один раз?

            p.s. извините, что столько комментов, но у вас их нельзя ни редактировать, ни удалять :(

          2. Вопросительный знак играет разную роль в зависимости от того, где он используется. Если он стоит после обычного символа или группы, то да — это означает «предыдущий элемент повторяется ноль или один раз».

            Например, под шаблон 'linu?x' подойдут как 'linux' так и 'linx'. Или, скажем, под 'lin (ux)?' подойдут как 'linux', так и 'lin'.

            А вот если '?' стоит после квантификатора '+' или '*', то здесь он играет роль «ограничителя жадности».

            Например, возьмите выражение '(.+) blabla'. Это выражение «захавает» не только то, что до 'blabla', но и 'blabla', и всё до конца строки, потому что выражение в скобках обозначает «ЛЮБОЙ символ один или более раз».

            Чтоб ограничить «жадность» регулярки, используется вопросительный знак после квантификатора, т. е.: '(.+?) blabla', которая говорит: 'любой символ один или более раз, НО не трогать то, что стоит после!'.

  10. А можно заставить nginx не кэшировать определенный URL?

    Например, если на upserver'e установлен WordPress, то прокэшируется и /wp-admin/*? Я правильно понимаю? ) Или админка будет работать нормально?)

Комментарии запрещены.