Кэширование обратного прокси для динамического содержимого
Я думал о том, чтобы спросить Рекомендации по использованию программного обеспечения, но потом я узнал, что это может быть слишком странная просьба, и сначала ей нужно уточнить.
Мои баллы:
- Каждый ответ содержит
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, но это не должно быть больше нескольких строк.