Кэширование обратного прокси для динамического содержимого

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

Мои баллы:

  • Каждый ответ содержит etag
    • который является хешем содержимого
    • и который глобально уникален (с достаточной вероятностью)
  • Содержимое (в основном) динамическое и может меняться в любое время (expires и max-age здесь бесполезны заголовки).
  • Содержимое частично зависит от пользователя, как указано разрешениями (которые сами иногда меняются).

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

Он должен выглядеть следующим образом: прокси всегда отправляет запрос на сервер, а затем

  • 1 сервер возвращает только etag, а прокси-сервер выполняет поиск по нему и
    • 1.1 при ударе кеша,
      • он считывает данные ответа из кеша
      • и отправляет ответ клиенту
    • 1.2 при пропуске кеша,
      • он снова запрашивает сервер, а затем
      • сервер возвращает ответ с содержимым и etag,
      • прокси хранит его в кеше
      • и отправляет ответ клиенту
  • 2 или сервер возвращает ответ с содержимым и etag,
    • прокси хранит данные в кеше
    • и отправляет ответ клиенту

Для простоты я отказался от обработки заголовка if-none-match, что довольно очевидно.

Моя причина в том, что наиболее распространенный случай 1.1 может быть очень эффективно реализован на сервере (используя его запросы на сопоставление кеша на etags, содержимое не кэшируется на сервере), так что большинство запросов могут быть обрабатывается без сервера, работающего с контентом ответа. Это должно быть лучше, чем сначала получать содержимое из бокового кеша, а затем обслуживать его.

В случае 1.2 на сервер есть два запроса, что звучит плохо, но не хуже, чем сервер запрашивает боковой кеш и получает промах.

Q1: Интересно, как сопоставить первый запрос HTTP. В случае 1 это похоже на запрос HEAD. В случае 2 это похоже на GET. Решение между ними зависит от сервера: если он может обслуживать etag без вычисления содержимого, то это случай 1, в противном случае это случай 2.

Q2: Есть ли обратный прокси-сервер, который делает что-то вроде этого? Я читал о nginx, HAProxy и Varnish, и, похоже, это не так. Это приводит меня к Q3:. Это плохая идея? Почему?

Q4: Если нет, то какой существующий прокси проще всего адаптировать?

Пример

Запрос GET, такой как /catalog/123/item/456 от пользователя U1, был подан с некоторым содержимым C1 и etag: 777777. Прокси хранит C1 под ключом 777777.

Теперь тот же запрос поступает от пользователя U2. Прокси перенаправляет его, сервер возвращает только etag: 777777, а прокси-серверу повезло, находит C1 в кеше (case 1.1) и отправляет его в U2. В этом примере ни клиенты, ни прокси-сервер не знали ожидаемого результата.

Интересной частью является то, как сервер мог знать etag без вычисления ответа. Например, у него может быть правило, указывающее, что запросы этой формы возвращают одинаковый результат для всех пользователей, предполагая, что данному пользователю разрешено его видеть. Поэтому, когда пришел запрос из U1, он вычислил C1 и сохранил etag под ключом /catalog/123/item/456. Когда тот же запрос пришел из U2, он просто подтвердил, что U2 разрешено видеть результат.

Ответ 1

Q1: это запрос GET. Сервер может ответить с "304 не измененным" без тела.

Q2: openresty (nginx с некоторыми дополнительными модулями) может это сделать, но вам понадобится для реализации некоторой логики (см. более подробное описание ниже).

Q3. Это звучит как разумная идея, учитывая информацию в вашем вопросе. Просто пища для размышлений:

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

  • Вы не должны ожидать, что кэш сохранит рассчитанные ответы навсегда. Таким образом, если сервер возвращает 304 not modified с etag: 777777 (в соответствии с вашим примером), но кеш не знает об этом, у вас должна быть возможность принудительно перестроить ответ, например. с другим запросом с пользовательским заголовком X-Force-Recalculate: true.

  • Не совсем часть вашего вопроса, но: Обязательно установите правильный заголовок Vary, чтобы предотвратить проблемы с кешированием.

  • Если это касается только разрешений, возможно, вы также можете работать с информацией о разрешении в подписанном файле cookie. Кэш может получить разрешение из файла cookie, не запрашивая сервер, и cookie является доказательством подделки из-за подписи.

Q4: для этого я бы использовал openresty, в частности модуль lua-resty-redis. Поместите кэшированный контент в redis key-value-store с ключом etag as. Вам нужно закодировать логику поиска в Lua, но это не должно быть больше нескольких строк.