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

Навигация по циклу

  1. Почему стоит переходить на Angie.

  2. Установка Angie из пакетов и в докере.

  3. Переезд с Nginx на Angie. Пошаговая инструкция.

  4. Настройка location в Angie. Разделение динамических и статических запросов.

  5. Перенаправления в Angie: return, rewrite и примеры их применения.

  6. Сжатие текста в Angie: статика, динамика, производительность.

  7. Серверное кэширование в Angie: тонкости настройки.

  8. Настройка TLS в Angie: безопасность и скорость.

  9. Настройка Angie в роли обратного HTTP-прокси.

  10. Балансировка нагрузки для HTTP(S) в Angie.

  11. Мониторинг Angie с помощью Console Light и API.

  12. Балансировка и проксирование L4-трафика в Angie.

  13. Клиентское кэширование в Angie.

  14. Динамические группы проксируемых серверов в Angie.

  15. Мониторинг Angie с Prometheus и Grafana.

  16. Отказоустойчивый кластер Angie с VRRP и Keepalived.

Видеоверсия

Для вашего удобства подготовлена видеоверсия этой статьи, доступна на Rutube, VKVideo и YouTube.

Принцип работы кэша в Angie

Сразу стоит оговориться, что в этой статье мы рассматриваем серверное кэширование ответов бэкенда с помощью модуля http_proxy. Аналогичные настройки есть в других модулях проксирования (fastcgi, scgi, uwsgi). Основная цель такого кэширования — сохранить ответ бэкенда и использовать его при повторных запросах клиентом того же ресурса. Кэш в Angie хранится на диске, при этом каждый элемент представляет собой файл. Путь к файлу и его имя определяются настройками зоны и ключа кэширования.

Здесь может возникнуть закономерный вопрос: почему не разместить кэш в оперативной памяти? Действительно, доступ к элементам кэша в памяти будет быстрее, но есть два аргумента против хранения кэша в RAM. Во‑первых, часто используемые файлы в большинстве операционных с��стем и так кэшируются при наличии свободной памяти, поэтому доступ к ним будет таким же быстрым. Во‑вторых, наличие кэша на диске даёт возможность быстрого старта без длительной фазы «прогрева» сервера. Ну и конечно, места на диске гораздо больше и можно значительно увеличить размер кэша.

Каждый элемент кэша имеет время актуальности и срок хранения на диске. Начальной загрузкой и удалением файлов управляют специальные процессы: cache loader и cache_manager соответственно.

Первое, что нужно настроить для использования серверного кэша — это зона кэширования.

Настройка зоны кэширования

Хранение кэша в Angie распределено по зонам. Каждая зона — это каталог внутри файловой системы, задаётся зона директивой proxy_cache_path. Разберём пример конфигурации зоны кэша:

http {
proxy_cache_path /var/www/cache levels=1:2 keys_zone=one:10m:file=/etc/angie/cache.state inactive=4h max_size=800m;
}

Первый параметр — это путь для хранения файлов кэша (/var/www/cache). Далее мы указываем количество уровней вложенности для разбиения файлов по директориям, максимально можно указать до трёх уровней вложенности. В каждом уровне можно указать 1 или 2 — это количество цифр в названии директории. Например при наших настройках файл кэша с именем 582f017bcde4b1e41623812ee6d93350 будет расположен в директории: /var/www/cache/0/35.

В параметре keys_zone задаётся название зоны (one), размер области разделяемой памяти для хранения ключей (10m), а также файл (cache.state) для хранения состояния ключей при перезагрузке процесса и последующего восстановления их в памяти. Хранение статуса кэша в файле появилось в Angie 1.9.0.

Следующий параметр (inactive) указывает, как долго хранить файлы кэша, если к ним не было обращений (4 часа). Наконец, задаём максимальный размер кэша (max_size) на диске (800 МБ). Также можно задать параметр минимально доступного сводного пространства на файловой системе с кэшем (min_free).

Для более тонкой настройки процессов загрузки и очистки кэша по нагрузке на диск можно использовать директивы manager/loader_files, manager/loader_threshold, manager/loader_sleep.

Настроив зону кэширования, можно переходить к настройке ключа кэширования.

Настройка ключа кэ��ирования

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

proxy_cache_key $scheme$host$uri;

В этой настройке ответ определяется по его полному адресу (схема, хост, URI без параметров). Если содержимое ответа зависит от других параметров запроса (заголовки, значения cookie и т. д.), достаточно добавить их в ключ. Например, добавим значение cookie с названием ID:

proxy_cache_key $scheme$host$uri$cookie_ID;

От значения ключа будет рассчитан MD5-хеш, который будет определять название файла и его размещение в зоне кэширования. Теперь мы готовы активировать кэш, то есть включить его в location с проксированием.

Подключение кэша в location

Логично подключать кэш в location, который проксируется на бэкенд. Основная директива, которая включает кэш — это proxy_cache. Но как правило мы хотим более тонко контролировать поведение кэша и типичная конфигурация будет выглядеть примерно так:

server {
  location / {
    proxy_cache one;
    proxy_cache_valid 200 10s;
    proxy_cache_lock on;
    proxy_cache_background_update on;
    proxy_cache_use_stale updating;
    proxy_pass	http://backend;
  }
}

Первая директива (proxy_cache) включает использование кэша с зоной one. Далее мы устанавливаем на уровне location время валидности (proxy_cache_valid) ответа в кэше и указываем кэшировать ответы с кодом 200 на 10 секунд. Можно указывать несколько директив proxy_cache_valid для различных кодов ответа. Также для работы кэширования обязательно должна быть включена буферизация ответа (proxy_buffering), что является значением по умолчанию.

Время кэширования определяется особенностями контента. Для редких изменений подходит большое время валидности кэша, а для динамического контента подходит минимальное время. Например, при нагрузке в 100 RPS кэширование на одну секунду даёт снижение нагрузки на бэкенд примерно в 100 раз.

Для оптимизации производительности включена блокировка при записи в кэш (proxy_cache_lock), что обеспечивает только один запрос к бэкенду для обновления элемента кэша, даже если есть несколько параллельных запросов от клиентов к данному ресурсу. Кроме того, включён режим фонового обновления кэша (proxy_cache_background_update), при котором клиенту быстро отдаётся устаревший ответ, а в это время происходит обновление кэша. Чтобы фоновое обновление кэша работало, необходима следующая директива (proxy_cache_use_stale), которая разрешает отдавать устаревший ответ из кэша.

На этом настройку кэширования можно считать законченной, но есть еще несколько важных аспектов, которые стоит рассмотреть. Начнём с обхода кэша и запроса кэширования определённых ответов.

Обход и запрет кэша

Типичной задачей при работе с кэшем является получение гарантированно свежего ответа, минуя кэш. Для этого существует директива proxy_cache_bypass. Например:

proxy_cache_bypass $cookie_forceupdate $arg_forceupdate;

При такой настройке достаточно установить cookie c названием forceupdate с непустым значением или добавить в запрос GET‑параметр forceupdate. Например: http://test/test?forceupdate=1.

Также важно понимать, что Angie использует заголовки ответа для принятия решения о кэшировании. То есть бэкенд может задать заголовки Cache‑Control, Expires, X‑Accel‑Expires и через них управлять кэшированием. Также не кэшируются ответы с заголовком Set‑Cookie и Vary «*». Обработку этих заголовков можно отключить с помощью директивы proxy_ignore_headers:

proxy_ignore_headers "X-Accel-Expires" "Expires" "Cache-Control";

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

Полностью запретить кэширование определённых ответов можно с помощью директивы proxy_no_cache, которая настраивается по аналогии с proxy_cache_bypass:

proxy_no_cache $cookie_nocache $arg_nocache$arg_comment;

В конфигурации выше запрещается кэшировать ответы при наличии cookie с именем nocache или при наличии параметров nocache или comment.

Ограничить кэш можно и по HTTP‑методам запроса, по умолчанию кэшируются GET и HEAD‑запросы, но можно расширить или сократить список методов, например:

proxy_cache_methods GET HEAD POST;

Таким образом, у нас существует широкий выбор средств тонкой настройки объектов кэширования. В Angie существует возможность использовать кэш в случае отказа бэкенда, рассмотрим такой вариант подробнее.

Обработка ошибок бэкенда

Допустим, бэкенд (или вся группа серверов) на момент запроса недоступны (нет подключения, 5xx ошибки и т.д.), но в кэше уже есть ответ (пусть и устаревший). Скорее всего, лучше отдать старый ответ, пока поднимаются бэкенды, чем ответить ошибкой пользователю. Настроить такое поведение можно с помощью директивы proxy_cache_use_stale, например с такими параметрами:

proxy_cache_use_stale updating error timeout invalid_header http_500 http_502 http_504;

Здесь довольно широкие полномочия по использованию устаревшего ответа из кэша: во время обновления кэша, при ошибке выбора сервера, при таймаутах по работе с сервером, при ошибках с ответом, при кодах ответа 500, 502 и 504.

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

Кэширование больших ответов

При работе с ответами большого размера (видео, архивы) полезно использовать модуль Slice, который умеет делить ответ на диапазоны. Дело в том, что при обычном алгоритме работы ответ будет помещен в кэш только после полного получения на стороне Angie. Далее ответ будет целиком размещён в одном файле на диске. То есть, получаем высокие задержки при записи кэша на диск и при обращении к нему.

Решение состоит в разделении ответа (slice), при этом кэширование начинает работать с частями ответа, что позволяет более гибко и эффективно применять кэш.

Конфигурация кэша с разделением представлена ниже.

location /video {
    slice             10m;
    proxy_cache       cache_slice;
    proxy_cache_key   $host$uri$slice_range;
    proxy_set_header  Range $slice_range;
    proxy_cache_valid 200 206 1h;
    proxy_pass        http://backend;
}

В этом примере задействовано разделение ответа на части по 10 МБ (slice 10m). Для корректной работы в ключ кэширования добавлена переменная $slice_range, а также заголовок Range. Теперь необходимо добавить кэширование ответов (proxy_cache_valid) с кодом 206 (Partial Content). На этом кэширование с разделением ответа настроено.

Точечная очистка кэша

Стандартный механизм кэширования не поддерживает точечную очистку элементов кэша средствами Angie. Но задачу можно решить сторонним модулем Cache Purge. Необходимо его установить и подключить в главную секцию конфига angie.conf:

apt install angie-module-cache-purge
load_module modules/ngx_http_cache_purge_module.so;

Теперь мы можем настроить сброс кэша по специальному запросу. Простейшая конфигурация для использования одного location выглядит так:

server {
  location / {
    proxy_cache one;
    proxy_pass	http://backend;
    proxy_cache_purge PURGE from 127.0.0.1;
  }
}

Здесь мы добавили возможность сброса кэша при запросе только с localhost. Запрос можно сформировать следующей командой:

curl -X PURGE http://127.1/*

Показанный выше запрос имеет метод PURGE для сброса кэша и будет очищать кэш по маске для URI /* (частичный ключ), причём звёздочку можно ставить только в конце. Важно, чтобы запрос на очистку по значению ключа совпадал с ключом кэша. В ответе можно увидеть успешный или неуспешный статус очистки и значения ключа, которые были очищены. Для корректной работы удаления кэша по маске последним параметром ключа кэширования должна быть переменная $uri.

Другой вариант настройки — отдельный location для очистки. То есть, запросы на очистку должны приходить на специальный адрес. При этом состав ключа для поиска удаляемых элементов указывается в директиве proxy_cache_purge. Пример показан ниже.

http {
  map $uri $wpurgeuri {
    "~/purge(?<wpurge>/.*)" $wpurge;
    default $uri;
  }
  server {
    location / {
      proxy_cache one;
      proxy_pass	http://backend;
      proxy_cache_purge PURGE from 127.0.0.1;
    }
    
    location /purge {
      allow 127.0.0.1;
      deny all;
      proxy_cache_purge one $wpurgeuri$is_args$args;
    }
  }
}

Здесь создаётся специальная переменная $wpurgeuri, которая будет служить ключом для очистки кэша, она формируется из $uri, но без префикса /purge.

Для такой конфигурации запрос на очистку кэша должен включать префикс /purge, например:

curl -X PURGE http://127.1/purge/*

Таким образом, мы получаем возможность частичной очистки кэша.

Логирование и мониторинг

Статистику по кэшу можно наблюдать по API или с помощью Angie Console Light. Также в Angie встроен экспортер для Prometheus для последующей визуализации в Grafana. Доступен процент попадания в кэш, его статус, объём файлов на диске, статистика по трафику и заполнение зоны разделяемой памяти.

Статус кэширования полезно фиксировать в логах. Для этого доступна переменная $upstream_cache_status.

Итоги

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

Следующая статья цикла: Настройка TLS в Angie: безопасность и скорость.