Перейти к содержанию

srcache: Прозрачная кэшировка на основе подзапросов для произвольных местоположений NGINX

Установка на Debian/Ubuntu

Эти документы применимы к APT пакету nginx-module-srcache, предоставляемому репозиторием GetPageSpeed Extras.

  1. Настройте репозиторий APT, как описано в настройке репозитория APT.
  2. Установите модуль:
sudo apt-get update
sudo apt-get install nginx-module-srcache
Показать версии и архитектуры
| Дистрибутив | Версия   | Компонент   | Архитектуры   |
|-------------|----------|-------------|-----------------|
| debian      | bookworm | main        | amd64, arm64    |
| debian      | trixie   | main        | amd64, arm64    |
| ubuntu      | focal    | main        | amd64, arm64    |
| ubuntu      | jammy    | main        | amd64, arm64    |
| ubuntu      | noble    | main        | amd64, arm64    |

ngx_srcache - Прозрачная кэшировка на основе подзапросов для произвольных местоположений NGINX

Статус

Этот модуль готов к производству.

Синопсис

 upstream my_memcached {
     server 10.62.136.7:11211;
     keepalive 10;
 }

 location = /memc {
     internal;

     memc_connect_timeout 100ms;
     memc_send_timeout 100ms;
     memc_read_timeout 100ms;
     memc_ignore_client_abort on;

     set $memc_key $query_string;
     set $memc_exptime 300;

     memc_pass my_memcached;
 }

 location /foo {
     set $key $uri$args;
     srcache_fetch GET /memc $key;
     srcache_store PUT /memc $key;
     srcache_store_statuses 200 301 302 307 308;

     # proxy_pass/fastcgi_pass/drizzle_pass/echo/etc...
     # или даже статические файлы на диске
 }
 location = /memc2 {
     internal;

     memc_connect_timeout 100ms;
     memc_send_timeout 100ms;
     memc_read_timeout 100ms;
     memc_ignore_client_abort on;

     set_unescape_uri $memc_key $arg_key;
     set $memc_exptime $arg_exptime;

     memc_pass unix:/tmp/memcached.sock;
 }

 location /bar {
     set_escape_uri $key $uri$args;
     srcache_fetch GET /memc2 key=$key;
     srcache_store PUT /memc2 key=$key&exptime=$srcache_expire;

     # proxy_pass/fastcgi_pass/drizzle_pass/echo/etc...
     # или даже статические файлы на диске
 }
 map $request_method $skip_fetch {
     default     0;
     POST        1;
     PUT         1;
 }

 server {
     listen 8080;

     location /api/ {
         set $key "$uri?$args";

         srcache_fetch GET /memc $key;
         srcache_store PUT /memc $key;

         srcache_methods GET PUT POST;
         srcache_fetch_skip $skip_fetch;

         # proxy_pass/drizzle_pass/content_by_lua/echo/...
     }
 }

Описание

Этот модуль предоставляет прозрачный уровень кэширования для произвольных местоположений NGINX (таких как те, которые используют upstream или даже обслуживают статические файлы на диске). Поведение кэширования в основном совместимо с RFC 2616.

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

Для основных запросов директива srcache_fetch работает в конце фазы доступа, поэтому директивы allow и deny стандартного модуля доступа ngx_http_access_module выполняются до нашей, что обычно является желаемым поведением по соображениям безопасности.

Рабочий процесс этого модуля выглядит следующим образом:

srcache flowchart

Кэширование подзапросов

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

Тем не менее, если вы используете lua-nginx-module, легко сделать кэширование подзапросов на Lua самостоятельно. То есть, сначала выполните подзапрос к местоположению memc-nginx-module для выполнения явного поиска кэша, если кэш попал, просто используйте возвращенные кэшированные данные; в противном случае выполните запрос к настоящему бэкенду и, наконец, вставьте данные в кэш.

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

Распределенное кэширование Memcached

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

 http {
     upstream moon {
         server 10.62.136.54:11211;
         server unix:/tmp/memcached.sock backup;
     }

     upstream earth {
         server 10.62.136.55:11211;
     }

     upstream sun {
         server 10.62.136.56:11211;
     }

     upstream_list universe moon earth sun;

     server {
         memc_connect_timeout 100ms;
         memc_send_timeout 100ms;
         memc_read_timeout 100ms;

         location = /memc {
             internal;

             set $memc_key $query_string;
             set_hashed_upstream $backend universe $memc_key;
             set $memc_exptime 3600; # в секундах
             memc_pass $backend;
         }

         location / {
             set $key $uri;
             srcache_fetch GET /memc $key;
             srcache_store PUT /memc $key;

             # proxy_pass/fastcgi_pass/content_by_lua/drizzle_pass/...
         }
     }
 }
Вот что происходит в приведенном выше примере: 1. Сначала мы определяем три upstream, moon, earth и sun. Это наши три сервера memcached. 1. Затем мы группируем их вместе как сущность списка upstream, названную universe, с помощью директивы upstream_list, предоставляемой set-misc-nginx-module. 1. После этого мы определяем внутреннее местоположение с именем /memc для общения с кластером memcached. 1. В этом местоположении /memc мы сначала устанавливаем переменную $memc_key со строкой запроса ($args), а затем используем директиву set_hashed_upstream, чтобы хешировать наш $memc_key по списку upstream universe, чтобы получить конкретное имя upstream, которому будет присвоена переменная $backend. 1. Мы передаем эту переменную $backend в директиву memc_pass. Переменная $backend может содержать значение moon, earth или sun. 1. Также мы определяем время истечения кэширования memcached как 3600 секунд (т.е. час), переопределив переменную $memc_exptime. 1. В нашем основном публичном местоположении / мы настраиваем переменную $uri как наш ключ кэша и затем настраиваем srcache_fetch для поиска в кэше и srcache_store для обновления кэша. Мы используем два подзапроса к нашему местоположению /memc, определенному ранее в этих двух директивах.

Можно использовать директивы set_by_lua или rewrite_by_lua из lua-nginx-module, чтобы внедрить пользовательский Lua-код для вычисления переменных $backend и/или $key в приведенном выше примере.

Следует помнить, что memcached имеет ограничения на длину ключей, т.е. 250 байт, так что для ключей, которые могут быть очень длинными, можно использовать директиву set_md5 или ее аналоги, чтобы предварительно хешировать ключ в фиксированное значение перед тем, как присвоить его $memc_key в местоположении /memc или аналогичном.

Кроме того, можно использовать директивы srcache_fetch_skip и srcache_store_skip, чтобы контролировать, что кэшировать, а что нет, на основе каждого запроса, и Lua также может использоваться здесь аналогично. Так что возможности действительно безграничны.

Чтобы максимизировать скорость, мы часто включаем пул соединений TCP (или сокетов Unix Domain) для наших upstream memcached, предоставляемых HttpUpstreamKeepaliveModule, например,

 upstream moon {
     server 10.62.136.54:11211;
     server unix:/tmp/memcached.sock backup;
     keepalive 10;
 }

где мы определяем пул соединений, который содержит до 10 соединений с поддержкой keep-alive (на каждый рабочий процесс nginx) для нашего upstream moon (кластера).

Кэширование с использованием Redis

Redis является альтернативным хранилищем ключ-значение с множеством дополнительных функций.

Вот рабочий пример с использованием модуля lua-resty-redis:

  location ~ '\.php$|^/update.php' {
    # настройка кэша
    set $key $request_uri;
    try_files $uri =404;

    srcache_fetch_skip $skip_cache;
    srcache_store_skip $skip_cache;

    srcache_response_cache_control off;
    srcache_store_statuses 200 201 301 302 307 308 404 503;

    set_escape_uri $escaped_key $key;

    srcache_fetch GET /redis-fetch $key;
    srcache_store PUT /redis-store key=$escaped_key;

    more_set_headers 'X-Cache-Fetch-Status $srcache_fetch_status';
    more_set_headers 'X-Cache-Store-Status $srcache_store_status';

    fastcgi_split_path_info ^(.+?\.php)(|/.*)$;
    # Замечание по безопасности: Если вы используете версию PHP, старше
    # последней 5.3, у вас должно быть "cgi.fix_pathinfo = 0;" в php.ini.
    # Смотрите http://serverfault.com/q/627903/94922 для деталей.
    include fastcgi_params;
    # Блокировка атак httproxy. Смотрите https://httpoxy.org/.
    fastcgi_param HTTP_PROXY "";
    fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param QUERY_STRING $query_string;
    fastcgi_intercept_errors on;

    fastcgi_pass upstream-name;
  }

  location /redis-fetch {
    internal;

    resolver 8.8.8.8 valid=300s;
    resolver_timeout 10s;

    content_by_lua_block {
      local key = assert(ngx.var.request_uri, "no key found")
      local redis = require "resty.redis"
      local red, err = redis:new()
      if not red then
        ngx.log(ngx.ERR, "Failed to create redis variable, error -> ", err)
        ngx.exit(500)
      end
      assert(red:connect("redis-master.default.svc.cluster.local", 6379))
      if not red then
        ngx.log(ngx.ERR, "Failed to connect to redis, error -> ", err)
        ngx.exit(500)
      end
      local res, err = red:auth("redispassword")
      if not res then
        ngx.say("failed to authenticate, ", err)
        ngx.exit(500)
      end
      local data = assert(red:get(key))
      assert(red:set_keepalive(10000, 100))
      if res == ngx.null then
        return ngx.exit(404)
      end
      ngx.print(data)
    }
  }

  location /redis-store {
    internal;

    resolver 8.8.8.8 valid=300s;
    resolver_timeout 10s;

    content_by_lua_block {
      local value = assert(ngx.req.get_body_data(), "no value found")
      local key = assert(ngx.var.request_uri, "no key found")
      local redis = require "resty.redis"
      local red, err = redis:new()
      if not red then
        ngx.log(ngx.ERR, "Failed to create redis variable, error -> ", err)
        ngx.exit(500)
      end
      assert(red:connect("redis-master.default.svc.cluster.local", 6379))
      if not red then
        ngx.log(ngx.ERR, "Failed to connect to redis, error -> ", err)
        ngx.exit(500)
      end
      local res, err = red:auth("redispassword")
      if not res then
        ngx.say("failed to authenticate, ", err)
        ngx.exit(500)
      end
      local data = assert(red:set(key, value))
      assert(red:set_keepalive(10000, 100))
      if res == ngx.null then
        return ngx.exit(404)
      end
    }
  }

Вот рабочий пример с использованием модулей HTTPRedis (запрос) и Redis2 (хранение):

 location /api {
     default_type text/css;

     set $key $uri;
     set_escape_uri $escaped_key $key;

     srcache_fetch GET /redis $key;
     srcache_store PUT /redis2 key=$escaped_key&exptime=120;

     # fastcgi_pass/proxy_pass/drizzle_pass/postgres_pass/echo/etc.
 }

 location = /redis {
     internal;

     set_md5 $redis_key $args;
     redis_pass 127.0.0.1:6379;
 }

 location = /redis2 {
     internal;

     set_unescape_uri $exptime $arg_exptime;
     set_unescape_uri $key $arg_key;
     set_md5 $key;

     redis2_query set $key $echo_request_body;
     redis2_query expire $key $exptime;
     redis2_pass 127.0.0.1:6379;
 }

Этот пример использует переменную $echo_request_body, предоставленную echo-nginx-module. Обратите внимание, что вам нужна последняя версия echo-nginx-module, v0.38rc2, поскольку более ранние версии могут работать ненадежно.

Кроме того, вам нужны как HttpRedisModule, так и redis2-nginx-module. Первый используется в подзапросе srcache_fetch, а второй — подзапросе srcache_store.

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

http://mailman.nginx.org/pipermail/nginx-devel/2012-March/002040.html

Обратите внимание, однако, если вы используете пакет OpenResty 1.0.15.3 или новее, то у вас уже есть все необходимое в этом пакете.

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

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

Рассмотрим следующую строку запроса URI

SID=BC3781C3-2E02-4A11-89CF-34E5CFE8B0EF&UID=44332&L=EN&M=1&H=1&UNC=0&SRC=LK

мы хотим удалить аргументы SID и UID из нее. Это легко достичь, если использовать lua-nginx-module одновременно:

 location = /t {
     rewrite_by_lua '
         local args = ngx.req.get_uri_args()
         args.SID = nil
         args.UID = nil
         ngx.req.set_uri_args(args)
     ';

     echo $args;
 }

Здесь мы используем директиву echo из echo-nginx-module, чтобы вывести окончательное значение $args в конце. Вы можете заменить это на ваши настройки srcache-nginx-module и настройки upstream вместо этого для вашего случая. Давайте протестируем этот интерфейс /t с помощью curl:

$ curl 'localhost:8081/t?RT=62&SID=BC3781C3-2E02-4A11-89CF-34E5CFE8B0EF&UID=44332&L=EN&M=1&H=1&UNC=0&SRC=LK'
M=1&UNC=0&RT=62&H=1&L=EN&SRC=LK

Стоит отметить, что если вы хотите сохранить порядок аргументов URI, вы можете сделать замены строк на значение $args непосредственно, например,

location = /t {
    rewrite_by_lua '
        local args = ngx.var.args
        newargs, n, err = ngx.re.gsub(args, [[\b[SU]ID=[^&]*&?]], "", "jo")
        if n and n > 0 then
            ngx.var.args = newargs
        end
    ';

    echo $args;
}

Теперь протестируйте это с оригинальной командой curl снова, и мы получаем именно то, что ожидаем:

RT=62&L=EN&M=1&H=1&UNC=0&SRC=LK

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

Директивы

srcache_fetch

синтаксис: srcache_fetch <метод> <uri> <аргументы>?

по умолчанию: нет

контекст: http, server, location, location if

фаза: post-access

Эта директива регистрирует обработчик фазы доступа, который будет выдавать подзапрос Nginx для просмотра кэша.

Когда подзапрос возвращает код состояния, отличный от 200, это означает пропуск кэша, и управление будет продолжаться к более поздним фазам, включая фазу контента, настроенную модулем ngx_http_proxy_module, ngx_http_fastcgi_module и другим. Если подзапрос возвращает 200 OK, то это сигнализирует о попадании в кэш, и этот модуль отправит ответ подзапроса как ответ текущего основного запроса клиенту напрямую.

Эта директива всегда будет выполняться в конце фазы доступа, так что директивы allow и deny модуля ngx_http_access_module всегда будут выполняться до этой.

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

srcache_fetch_skip

синтаксис: srcache_fetch_skip <флаг>

по умолчанию: srcache_fetch_skip 0

контекст: http, server, location, location if

фаза: post-access

Аргумент <флаг> поддерживает переменные nginx. Когда значение этого аргумента не пустое и не равно 0, процесс извлечения будет безусловно пропущен.

Например, чтобы пропустить кэшированные запросы, у которых есть cookie с именем foo и значением bar, мы можем написать

 location / {
     set $key ...;
     set_by_lua $skip '
         if ngx.var.cookie_foo == "bar" then
             return 1
         end
         return 0
     ';

     srcache_fetch_skip $skip;
     srcache_store_skip $skip;

     srcache_fetch GET /memc $key;
     srcache_store GET /memc $key;

     # proxy_pass/fastcgi_pass/content_by_lua/...
 }
где lua-nginx-module используется для вычисления значения переменной $skip на (более ранней) фазе переписывания. Аналогично, переменная $key также может быть рассчитана с помощью Lua, используя директиву set_by_lua или rewrite_by_lua.

Стандартная директива map также может быть использована для вычисления значения переменной $skip, использованной в приведенном выше примере:

 map $cookie_foo $skip {
     default     0;
     bar         1;
 }

но ваше выражение map должно быть помещено в блок конфигурации http в вашем файле nginx.conf.

srcache_store

синтаксис: srcache_store <метод> <uri> <аргументы>?

по умолчанию: нет

контекст: http, server, location, location if

фаза: output-filter

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

Вы можете использовать директивы srcache_store_skip и srcache_store_max_size, чтобы отключить кэширование для определенных запросов в случае пропуска кэша.

Начиная с выпуска v0.12rc7, строки состояния ответа, заголовки ответа и тела ответа будут помещены в кэш. По умолчанию следующие специальные заголовки ответа не будут кешироваться:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailers
  • Transfer-Encoding
  • Upgrade
  • Set-Cookie

Вы можете использовать директивы srcache_store_pass_header и/или srcache_store_hide_header, чтобы контролировать, какие заголовки кэшировать, а какие нет.

Данные оригинального ответа будут отправлены, как только они будут получены. srcache_store просто копирует и собирает данные в фильтре вывода, не откладывая их отправку далее.

Но обратите внимание, что хотя все данные ответа будут отправлены немедленно, текущий жизненный цикл запроса Nginx не завершится, пока подзапрос srcache_store не завершится. Это означает задержку в закрытии TCP соединения на стороне сервера (когда HTTP keepalive отключен, но правильные HTTP клиенты должны активно закрывать соединение на стороне клиента, что не создает дополнительной задержки или других проблем вообще) или обслуживание следующего запроса, отправленного на том же TCP соединении (когда HTTP keepalive активно).

srcache_store_max_size

синтаксис: srcache_store_max_size <размер>

по умолчанию: srcache_store_max_size 0

контекст: http, server, location, location if

фаза: output-header-filter

Когда длина тела ответа превышает этот размер, этот модуль не будет пытаться сохранить тело ответа в кэш с использованием шаблона подзапроса, указанного в srcache_store.

Это особенно полезно при использовании хранилища кэша, которое имеет жесткий верхний предел на входные данные. Например, сервер Memcached имеет ограничение по умолчанию в 1 MB на элемент.

Когда указывается 0 (значение по умолчанию), проверка ограничений не производится.

srcache_store_skip

синтаксис: srcache_store_skip <флаг>

по умолчанию: srcache_store_skip 0

контекст: http, server, location, location if

фаза: output-header-filter

Аргумент <флаг> поддерживает переменные Nginx. Когда значение этого аргумента не пустое и не равно 0, то процесс сохранения будет безусловно пропущен.

Начиная с версии v0.25, выражение <флаг> (возможно содержащее переменные Nginx) может оцениваться до двух раз: первый раз сразу после отправки заголовка ответа, и когда выражение <флаг> не оценивается как истинные значения, оно будет оценено снова сразу после того, как конец потока данных тела ответа будет получен. До v0.25 выполнялась только первая оценка.

Вот пример использования Lua для установки $nocache, чтобы избежать сохранения URI, которые содержат строку "/tmp":

 set_by_lua $nocache '
     if string.match(ngx.var.uri, "/tmp") then
         return 1
     end
     return 0';

 srcache_store_skip $nocache;

srcache_store_statuses

синтаксис: srcache_store_statuses <статус1> <статус2> ..

по умолчанию: srcache_store_statuses 200 301 302 307 308

контекст: http, server, location, location if

фаза: output-header-filter

Эта директива контролирует, какие ответы следует сохранять в кэше согласно их коду состояния.

По умолчанию в кэш будут сохранены только ответы 200, 301, 302, 307 и 308, а все остальные ответы пропустят srcache_store.

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

 srcache_store_statuses 200 201 301 302 307 308 404 503;

По крайней мере, один аргумент должен быть предоставлен для этой директивы.

Эта директива была впервые введена в выпуске v0.13rc2.

srcache_store_ranges

синтаксис: srcache_store_ranges on|off

по умолчанию: srcache_store_ranges off

контекст: http, server, location, location if

фаза: output-body-filter

Когда эта директива включена (по умолчанию off), srcache_store также будет сохранять 206 Partial Content ответы, сгенерированные стандартным ngx_http_range_filter_module. Если вы включите эту директиву, вы ДОЛЖНЫ добавить $http_range в ваши ключи кэша. Например,

 location / {
     set $key "$uri$args$http_range";
     srcache_fetch GET /memc $key;
     srcache_store PUT /memc $key;
 }

Эта директива была впервые введена в выпуске v0.27.

srcache_header_buffer_size

синтаксис: srcache_header_buffer_size <размер>

по умолчанию: srcache_header_buffer_size 4k/8k

контекст: http, server, location, location if

фаза: output-header-filter

Эта директива контролирует буфер заголовков при сериализации заголовков ответа для srcache_store. Размер по умолчанию - это размер страницы, обычно 4k или 8k, в зависимости от конкретных платформ.

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

Эта директива была впервые введена в выпуске v0.12rc7.

srcache_store_hide_header

синтаксис: srcache_store_hide_header <заголовок>

по умолчанию: нет

контекст: http, server, location, location if

фаза: output-header-filter

По умолчанию этот модуль кэширует все заголовки ответа, кроме следующих:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailers
  • Transfer-Encoding
  • Upgrade
  • Set-Cookie

Вы можете скрыть еще больше заголовков ответа от srcache_store, перечислив их имена (регистр не важен) с помощью этой директивы. Например,

 srcache_store_hide_header X-Foo;
 srcache_store_hide_header Last-Modified;

Разрешено несколько вхождений этой директивы в одном местоположении.

Эта директива была впервые введена в выпуске v0.12rc7.

Смотрите также srcache_store_pass_header.

srcache_store_pass_header

синтаксис: srcache_store_pass_header <заголовок>

по умолчанию: нет

контекст: http, server, location, location if

фаза: output-header-filter

По умолчанию этот модуль кэширует все заголовки ответа, кроме следующих:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailers
  • Transfer-Encoding
  • Upgrade
  • Set-Cookie

Вы можете заставить srcache_store сохранить один или несколько из этих заголовков ответа, указав их имена (регистр не важен) с помощью этой директивы. Например,

 srcache_store_pass_header Set-Cookie;
 srcache_store_pass_header Proxy-Autenticate;

Разрешено несколько вхождений этой директивы в одном местоположении.

Эта директива была впервые введена в выпуске v0.12rc7.

Смотрите также srcache_store_hide_header.

srcache_methods

синтаксис: srcache_methods <метод>...

по умолчанию: srcache_methods GET HEAD

контекст: http, server, location

фаза: post-access, output-header-filter

Эта директива указывает методы HTTP запросов, которые учитываются либо srcache_fetch, либо srcache_store. HTTP методы запросов, не перечисленные, будут полностью пропущены из кэша.

Следующие HTTP методы разрешены: GET, HEAD, POST, PUT и DELETE. Методы GET и HEAD всегда неявно включены в список независимо от их наличия в этой директиве.

Обратите внимание, что начиная с релиза v0.17 запросы HEAD всегда пропускаются srcache_store, поскольку их ответы никогда не содержат тело ответа.

Эта директива была впервые введена в выпуске v0.12rc7.

srcache_ignore_content_encoding

синтаксис: srcache_ignore_content_encoding on|off

по умолчанию: srcache_ignore_content_encoding off

контекст: http, server, location, location if

фаза: output-header-filter

Когда эта директива выключена (что является значением по умолчанию), непустой заголовок ответа Content-Encoding приведет к тому, что srcache_store пропустит сохранение всего ответа в кэш и выдаст предупреждение в файле error.log Nginx, подобное этому:

[warn] 12500#0: *1 srcache_store skipped due to response header "Content-Encoding: gzip"
            (maybe you forgot to disable compression on the backend?)

Включение этой директивы игнорирует заголовок ответа Content-Encoding и сохраняет ответ как обычно (и также без предупреждения).

Рекомендуется всегда отключать сжатие gzip/deflate на вашем сервере бэкенда, указав следующую строку в вашем файле nginx.conf:

 proxy_set_header  Accept-Encoding  "";

Эта директива была впервые введена в выпуске v0.12rc7.

srcache_request_cache_control

синтаксис: srcache_request_cache_control on|off

по умолчанию: srcache_request_cache_control off

контекст: http, server, location

фаза: post-access, output-header-filter

Когда эта директива включена, заголовки запроса Cache-Control и Pragma будут учитываться этим модулем следующим образом:

  1. srcache_fetch, т.е. операция поиска кэша, будет пропущена, когда заголовки запроса Cache-Control: no-cache и/или Pragma: no-cache присутствуют.
  2. srcache_store, т.е. операция хранения кэша, будет пропущена, когда заголовок запроса Cache-Control: no-store указан.

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

Эта директива была впервые введена в выпуске v0.12rc7.

Смотрите также srcache_response_cache_control.

srcache_response_cache_control

синтаксис: srcache_response_cache_control on|off

по умолчанию: srcache_response_cache_control on

контекст: http, server, location

фаза: output-header-filter

Когда эта директива включена, заголовки ответа Cache-Control и Expires будут учитываться этим модулем следующим образом:

  • Cache-Control: private пропускает srcache_store,
  • Cache-Control: no-store пропускает srcache_store,
  • Cache-Control: no-cache пропускает srcache_store,
  • Cache-Control: max-age=0 пропускает srcache_store,
  • и Expires: <дата, не более недавняя, чем сейчас> пропускает srcache_store.

Эта директива имеет приоритет перед директивами srcache_store_no_store, srcache_store_no_cache и srcache_store_private.

Эта директива была впервые введена в выпуске v0.12rc7.

Смотрите также srcache_request_cache_control.

srcache_store_no_store

синтаксис: srcache_store_no_store on|off

по умолчанию: srcache_store_no_store off

контекст: http, server, location

фаза: output-header-filter

Включение этой директивы заставит ответы с заголовком Cache-Control: no-store сохраняться в кэше, когда srcache_response_cache_control включен и выполнены другие условия. По умолчанию отключено.

Эта директива была впервые введена в выпуске v0.12rc7.

srcache_store_no_cache

синтаксис: srcache_store_no_cache on|off

по умолчанию: srcache_store_no_cache off

контекст: http, server, location

фаза: output-header-filter

Включение этой директивы заставит ответы с заголовком Cache-Control: no-cache сохраняться в кэше, когда srcache_response_cache_control включен и выполнены другие условия. По умолчанию отключено.

Эта директива была впервые введена в выпуске v0.12rc7.

srcache_store_private

синтаксис: srcache_store_private on|off

по умолчанию: srcache_store_private off

контекст: http, server, location

фаза: output-header-filter

Включение этой директивы заставит ответы с заголовком Cache-Control: private сохраняться в кэше, когда srcache_response_cache_control включен и выполнены другие условия. По умолчанию отключено.

Эта директива была впервые введена в выпуске v0.12rc7.

srcache_default_expire

синтаксис: srcache_default_expire <время>

по умолчанию: srcache_default_expire 60s

контекст: http, server, location, location if

фаза: output-header-filter

Эта директива управляет периодом времени по умолчанию, который разрешен для значения переменной $srcache_expire, когда в заголовках ответа не указаны ни Cache-Control: max-age=N, ни Expires.

Аргументы <время> указаны в секундах по умолчанию. Но разумно всегда явно указывать единицу времени, чтобы избежать недоразумений. Поддерживаемые единицы времени: "s" (секунды), "ms" (миллисекунды), "y" (годы), "M" (месяцы), "w" (недели), "d" (дни), "h" (часы) и "m" (минуты). Например,

 srcache_default_expire 30m; # 30 минут

Это время должно быть менее 597 часов.

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

Эта директива была впервые введена в выпуске v0.12rc7.

srcache_max_expire

синтаксис: srcache_max_expire <время>

по умолчанию: srcache_max_expire 0

контекст: http, server, location, location if

фаза: output-header-filter

Эта директива управляет максимальным периодом времени истечения, который разрешен для значения переменной $srcache_expire. Эта настройка имеет приоритет над другими методами вычисления.

Аргументы <время> указаны в секундах по умолчанию. Но разумно всегда явно указывать единицу времени, чтобы избежать недоразумений. Поддерживаемые единицы времени: "s" (секунды), "ms" (миллисекунды), "y" (годы), "M" (месяцы), "w" (недели), "d" (дни), "h" (часы) и "m" (минуты). Например,

 srcache_max_expire 2h;  # 2 часа

Это время должно быть менее 597 часов.

Когда указывается 0 (значение по умолчанию), то не будет никаких ограничений вообще.

Эта директива была впервые введена в выпуске v0.12rc7.

Переменные

$srcache_expire

тип: целое число

кэшируемая: нет

записываемая: нет

Эта переменная Nginx определяет рекомендуемый период времени истечения (в секундах) для текущего ответа, который будет сохранен в кэше. Алгоритм вычисления значения следующий:

  1. Когда в заголовке ответа указан Cache-Control: max-age=N, тогда N будет использован как время истечения,
  2. в противном случае, если заголовок ответа Expires указан, тогда время истечения будет получено путем вычитания текущей временной метки из времени, указанного в заголовке Expires,
  3. когда ни заголовки Cache-Control: max-age=N, ни Expires не указаны, используйте значение, указанное в директиве srcache_default_expire.

Конечное значение этой переменной будет равно значению, указанному директивой srcache_max_expire, если значение, полученное в алгоритме выше, превышает максимальное значение (если таковое имеется).

Вам не обязательно использовать эту переменную для времени истечения.

Эта переменная была впервые введена в выпуске v0.12rc7.

$srcache_fetch_status

тип: строка

кэшируемая: нет

записываемая: нет

Эта переменная Nginx оценивается в статус фазы "поиска" для системы кэширования. Возможны три значения: HIT, MISS и BYPASS.

Когда подзапрос "поиска" возвращает код состояния, отличный от 200, или его ответные данные не являются корректными, эта переменная оценивается в значение MISS.

Значение этой переменной имеет смысл только после фазы обработки запроса access, или BYPASS всегда задано.

Эта переменная была впервые введена в выпуске v0.14.

$srcache_store_status

тип: строка

кэшируемая: нет

записываемая: нет

Эта переменная Nginx дает текущий статус кэширования для фазы "хранения". Два возможных значения - STORE и BYPASS.

Поскольку ответы подзапроса "хранения" всегда игнорируются, значение этой переменной всегда будет STORE, пока подзапрос "хранения" фактически выполняется.

Значение этой переменной имеет смысл только тогда, когда заголовки запроса текущего (основного) запроса отправляются. Окончательный результат можно получить только после того, как все тело ответа было отправлено, если заголовок ответа Content-Length не был указан для основного запроса.

Эта переменная была впервые введена в выпуске v0.14.

Известные проблемы

  • В некоторых системах включение aio и/или sendfile может остановить работу srcache_store. Вы можете отключить их в местоположениях, сконфигурированных с помощью srcache_store.
  • Директива srcache_store не может использоваться для захвата ответов, сгенерированных подзапросами директив echo-nginx-module таких как echo_subrequest_async и echo_location. Рекомендуется использовать HttpLuaModule для инициализации и захвата подзапросов, что должно работать с srcache_store.

Предостережения

  • Рекомендуется отключить сжатие gzip на вашем сервере бэкенда и использовать модуль nginx ngx_http_gzip_module для выполнения этой задачи. В случае ngx_http_proxy_module вы можете использовать следующую настройку конфигурации для отключения gzip сжатия на бэкенде:
     proxy_set_header  Accept-Encoding  "";
    
  • Не используйте директиву if модуля ngx_http_rewrite_module в том же местоположении, что и этот модуль, потому что "if — это зло". Вместо этого используйте ngx_http_map_module или lua-nginx-module в сочетании с директивами srcache_store_skip и/или srcache_fetch_skip этого модуля. Например:
     map $request_method $skip_fetch {
         default     0;
         POST        1;
         PUT         1;
     }
    
     server {
         listen 8080;
    
         location /api/ {
             set $key "$uri?$args";
    
             srcache_fetch GET /memc $key;
             srcache_store PUT /memc $key;
    
             srcache_methods GET PUT POST;
             srcache_fetch_skip $skip_fetch;
    
             # proxy_pass/drizzle_pass/content_by_lua/echo/...
         }
     }
    

Устранение неполадок

Чтобы отладить проблемы, вы всегда должны сначала проверять ваш файл error.log Nginx. Если ошибки не обнаружены, вам нужно включить отладочные логи Nginx для получения дополнительных деталей, как объяснено в debugging log.

Несколько распространенных ловушек для новичков:

  • Оригинальный ответ содержит заголовок Cache-Control, который явно отключает кэширование, и вы не настраиваете такие директивы, как srcache_response_cache_control.
  • Оригинальный ответ уже сжат с помощью gzip, который по умолчанию не кешируется (см. srcache_ignore_content_encoding).
  • Memcached может вернуть CLIENT_ERROR bad command line format, когда используется слишком длинный ключ (250 символов по состоянию на версию 1.4.25). Поэтому безопаснее использовать set_md5 $key $uri$args; вместо set $key $uri$args;. Директива set_md5 (и другие) доступны в модуле OpenResty set-misc.
  • Nginx может вернуть client intended to send too large body, когда пытается сохранить объекты больше 1 MB к хранилищу, в этом случае значение client_max_body_size Nginx должно быть установлено на более высокое значение.
  • Memcached может не успевать сохранить объекты больше 1 MB, вызывая ошибки, такие как srcache_store subrequest failed status=502. Начиная с версии 1.4.2, memcached поддерживает командную строку -I, чтобы переопределить размер каждой страницы по умолчанию. Пожалуйста, прочтите его страницу man для получения более подробной информации.

Тестовый набор

Этот модуль поставляется вместе с тестовым набором на Perl. Тестовые случаи также являются декларативными. Спасибо Test::Nginx из мира Perl.

Чтобы запустить его у себя:

 $ PATH=/path/to/your/nginx-with-srcache-module:$PATH prove -r t
Вам нужно завершить все процессы Nginx перед запуском тестового набора, если вы изменили двоичный файл сервера Nginx.

Поскольку один сервер nginx (по умолчанию, localhost:1984) используется во всех тестовых сценариях (.t файлы), не имеет смысла запускать тестовый набор параллельно, указывая -jN, когда вызывается утилита prove.

Некоторые части тестового набора требуют, чтобы были включены модули ngx_http_rewrite_module, echo-nginx-module, rds-json-nginx-module и drizzle-nginx-module при сборке Nginx.

Смотрите также