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

8

Я кеширую динамически генерируемые страницы (PHP-FPM, NGINX) и перед ними лак, это работает очень хорошо.

Однако, как только тайм-аут кэша достигнут, я вижу это:

  • страница новых запросов клиентов
  • лак распознает тайм-аут кэша
  • клиент ждет
  • лак выбирает новую страницу из бэкэнда
  • Лак доставляет новую страницу клиенту (и имеет страницу в кеше, также для следующего запроса, который получает его мгновенно)

Что я хотел бы сделать, это:

  • страница клиентских запросов
  • лак распознает тайм-аут
  • лак доставляет старую страницу клиенту
  • лак выбирает новую страницу из бэкэнда и помещает ее в кеш

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

Тем не менее, я не хочу наказывать пользователя ждать в очереди и скорее доставить что-то немедленно. Это возможно каким-то образом?

Чтобы проиллюстрировать это, приведем пример вывода команды «Осада 5 минут» на моем сервере, который был настроен для кэширования в течение одной минуты:

HTTP/1.1,200,  1.97,  12710,/,1,2013-06-24 00:21:06
...
HTTP/1.1,200,  1.88,  12710,/,1,2013-06-24 00:21:20
...
HTTP/1.1,200,  1.93,  12710,/,1,2013-06-24 00:22:08
...
HTTP/1.1,200,  1.89,  12710,/,1,2013-06-24 00:22:22
...
HTTP/1.1,200,  1.94,  12710,/,1,2013-06-24 00:23:10
...
HTTP/1.1,200,  1.91,  12709,/,1,2013-06-24 00:23:23
...
HTTP/1.1,200,  1.93,  12710,/,1,2013-06-24 00:24:12
...

Я пропустил сотни запросов, запущенных 0.02или около того. Но все равно меня беспокоит, что будут пользователи, которым придется ждать почти 2 секунды своего необработанного HTML.

Разве мы не можем сделать лучше здесь?

(Я наткнулся на Varnish send в то время как кеш , это звучало похоже, но не совсем то, что я пытаюсь сделать.)

Решение

Ответ от Шейна Мэддена содержал решение, но я не сразу понял это. Была еще одна деталь, которую я не включил в свой вопрос, потому что я думал, что это не имеет отношения к делу, но на самом деле это так.

Решение CMS, которое я сейчас использую, имеет прослушиватель базы данных лака и, следовательно, имеет возможность уведомлять лак о запрете страниц, содержание которых изменилось. Он отправил PURGEзапрос с некоторым регулярным выражением, чтобы запретить определенные страницы.

Чтобы подвести итог, есть два случая, когда я получил незадачливых пользователей:

  1. истекает нормальный лак TTL страницы
  2. бэкэнд-пользователи меняют контент, отправляет запрос на очистку для лака

В обоих случаях у меня «неудачливые» пользователи. Во втором случае это облегчается тем фактом, что бэкэнд-пользователи обычно проверяют страницу после ее изменения; но не обязательно.

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

Вместо отправки запроса на очистку я использовал предложение Шейна и настроил VCL так, чтобы мой слушатель базы данных лака мог отправить специальный запрос для получения страницы с hash_always_missустановленным значением true.

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

Результатом было то, что слушатель базы данных отправил запрос на лакировку, и пока я опрашивал определенную страницу, мои запросы никогда не делались «неудачными», но однажды лак полностью извлек страницу из бэкэнда (это может варьироваться от 300 мс до 2 с). вдруг был там.

Я все еще должен найти решение, как избежать тех же проблем, когда нормальный TTL заканчивается, но я думаю, что решение также точно так, как предлагает Шейн: использование wget для запуска hash_always_miss , я просто должен быть достаточно умен, чтобы получить список страниц я должен обновить.

отметка
источник

Ответы:

3

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

В моем случае это связано wgetс тем, что cron отправляет специальный заголовок для отметки запросов и настройки req.hash_always_missна основе этого, заставляя новую копию содержимого извлекаться в кэш.

acl purge {
    "localhost";
}

sub vcl_recv {
    /* other config here */
    if (req.http.X-Varnish-Nuke == "1" && client.ip ~ purge) {
        set req.hash_always_miss = true;
    }
    /* ... */
}

Для вашего контента это может означать, что для Varnish TTL будет установлено значение около 5 минут, но cron'd будет настроен на выполнение запроса на обновление кэша каждую минуту.

Шейн Мэдден
источник
Я думаю, я понимаю, что вы имеете в виду. Это было бы хорошо для одной страницы, но для других тысяч? Не могу использовать cron / wget в этом масштабе.
отметьте
По сути, вы должны объявить, по крайней мере, какие страницы вы хотите сохранить в кеше. Учитывая этот список, нет причин, по которым wget в cron-скрипте не может вам помочь.
Сокол Момот
Я думаю, что это проблема с этим "внешним подходом лака". В конечном итоге мне нужно сесть и принять решение и реализовать это для каждой страницы. Нет никакого способа сказать лаку: "ttl закончился? Принесите новую версию, но пока это не произошло, подайте старую версию" ...? Хм.
отметьте
4

@РЕДАКТИРОВАТЬ:

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


Ну, я не уверен на 100%, почему предыдущие комментарии говорят, что это не работает, но согласно: https://www.varnish-software.com/static/book/Saving_a_request.html

  • req.grace - определяет, как долго объект может быть просрочен, чтобы Varnish все еще рассматривал его в режиме отсрочки.
  • beresp.grace - определяет, как долго Varnish beresp.ttl-time будет хранить объект.
  • req.grace - часто изменяется в vcl_recv в зависимости от состояния бэкэнда.

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

sub vcl_recv {
    # Cache rules above here...
    if (req.backend.healthy) {
        set req.grace = 30d;
    } else {
        set req.grace = 300d;
    }
}

sub vcl_fetch {
    # Fetch rules above here ...

    # If backend returns 500 error then boost the cache grace period...
    if (beresp.status == 500) {
        set beresp.grace = 10h;
        return (restart);
    }

    # How long carnish should keep the objects in cache..
    set beresp.grace = 1h;

    # Actual TTL of cache - If an object is older than this an update will be triggered to the backend server :)
    set beresp.ttl = 1m;
}

Обратите внимание, что если вы хотите предоставить более длительный период отсрочки ответа бэкэнда (для 500 ошибок, как в моем конфиге), вам необходимо настроить бэкэнд-зондирование ... Вот копия моего бэкэнд-зондирования.

backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .probe = { 
        .url = "/nginx-status";
        .timeout = 500 ms; 
        .interval = 3s; 
        .window = 10;
        .threshold = 4;
    }
}
Дэвид
источник
это все еще действует для vcl 4.0?
Гаделкарем
0

В Varnish 3 это достигается с помощью «Grace mode». Согласно инструкции [1], вы должны добавить следующую логику:

sub vcl_fetch {
  set beresp.grace = 30m;
} // Makes objects to be cached/stored 30 min beyond its max-age/ttl

sub vcl_recv {
  set req.grace = 60s;
} // Allows varnish to serve objects which expired within last minute.

[1] https://www.varnish-cache.org/docs/3.0/tutorial/handling_misbehaving_servers.html#grace-mode

NITEMAN
источник
Это не приведет к достижению цели OP, один клиент все равно будет отложен в ожидании получения содержимого из бэкэнда, даже если другие клиенты за ним получат устаревший результат. «После истечения TTL первый клиент, запрашивающий контент, должен застрять на 15 секунд, а второй клиент должен получить льготную копию». - varnish-software.com/static/book/Saving_a_request.html
Pax,
Извините, я не понял, как это работает с первым просроченным запросом.
НИТЕМАН
Может быть, желаемая цель может быть достигнута с помощью некоторых ttl cheat & inline C, чтобы запустить «фоновый запрос»
NITEMAN