Я запутался в кэшировании HTTP

Я думал о пакетном чтении и записи в среде RESTful и, кажется, пришел к выводу, что у меня есть более общие вопросы о кэшировании HTTP. (Ниже я использую запятые (",") для разделения идентификаторов нескольких записей, но эта деталь не относится к обсуждению.)

Я начал с этой проблемы:

1. Один GET признан недействительным при пакетном обновлении

GET /farms/123         # get info about Old MacDonald's Farm
PUT /farms/123,234,345 # update info on Old MacDonald's Farm and some others
GET /farms/123

Как сервер кэширования между клиентом и сервером Farms узнает, что его кеш /farms/123 становится недействительным, когда он видит PUT?

Потом я понял, что это тоже проблема:

2. Пакет GET аннулирован единичным (или пакетным) обновлением

GET /farms/123,234,345 # get info about a few farms
PUT /farms/123         # update Old MacDonald's Farm
GET /farms/123,234,345

Как кеш узнает, что нужно аннулировать множественную ферму GET, когда он видит, что PUT проходит?

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

3. Один GET признан недействительным в результате обновления связанной записи.

GET /farms/123   # get info about Old MacDonald's Farm
PUT /farmers/987 # Old MacDonald sells his farm and buys another one
GET /farms/123

Как кеш узнает, что единственный GET должен быть признан недействительным, когда он видит, что PUT проходит?

Даже если вы измените модели на более RESTful, используя модели отношений, вы получите ту же проблему:

GET    /farms/123           # get info about Old MacDonald's Farm
DELETE /farm_ownerships/456 # Old MacDonald sells his farm...
POST   /farm_ownerships     # and buys another one
GET    /farms/123

В обеих версиях #3 первый GET должен возвращать что-то вроде (в JSON):

farm: {
  id: 123,
  name: "Shady Acres",
  size: "60 acres",
  farmer_id: 987
}

И второй GET должен вернуть что-то вроде:

farm: {
  id: 123,
  name: "Shady Acres",
  size: "60 acres",
  farmer_id: null
}

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

Так есть ли заголовки, которые мне не хватает? Вещи, указывающие на то, что кеш должен выполнять HEAD перед любыми GET для определенных ресурсов? Я полагаю, что мог бы жить с двойными запросами для каждого ресурса, если бы я мог сообщить кешам, какие ресурсы, вероятно, будут часто обновляться.

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


person James A. Rosen    schedule 11.01.2009    source источник
comment
Только что создав быстродействующее веб-приложение ajax, мне очень интересно, как будет дан ответ на этот вопрос. +1 и звезда!   -  person Karl    schedule 11.01.2009
comment
Возможно, то, что я ищу, является недействительным заголовком-другим, который сервер может добавить при возврате из PUT, POST или DELETE. Увы, похоже, что его нет.   -  person James A. Rosen    schedule 11.01.2009
comment
В случае HTTPS прокси не видят ничего, кроме имени хоста и порта. Они не видят ни заголовков, ни pathInfo, поэтому проблемы не существует. +++ Invalidates-Other было именно то, о чем я изо всех сил пытался сформулировать вопрос, прежде чем нашел ваш отличный вопрос.   -  person maaartinus    schedule 22.10.2016


Ответы (5)


Кэш-серверы должны аннулировать объект, на который ссылается URI, при получении PUT (но, как вы заметили, это не распространяется на все случаи).

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

Это по-прежнему очень сложный вопрос, над которым все еще ведется работа (например, см. http://www.ietf.org/internet-drafts/draft-ietf-httpbis-p6-cache-05.txt)

Кэширование в прокси-серверах на самом деле не применяется, если контент зашифрован (по крайней мере, с помощью SSL), поэтому это не должно быть проблемой (хотя проблема может быть на клиенте).

person frankodwyer    schedule 11.01.2009
comment
В исходном вопросе не упоминаются кеш-серверы, я думаю, что речь шла о локальном кеше браузера. - person Karl; 11.01.2009
comment
Нет, мой первоначальный вопрос гласит: как сервер кэширования между клиентом и сервером Farms узнает, что его кеш /farms/123 недействителен, когда он видит PUT? Я имел в виду как кэш-серверы, так и локальные кэши. - person James A. Rosen; 11.01.2009
comment
Re: SSL: см. мой комментарий о зашифрованном контенте по незашифрованным каналам. - person James A. Rosen; 11.01.2009

Протокол HTTP поддерживает тип запроса под названием «If-Modified-Since», который в основном позволяет серверу кэширования запрашивать веб-сервер, изменился ли элемент. Протокол HTTP также поддерживает заголовки «Cache-Control» внутри ответов HTTP-сервера, которые сообщают серверам кеша, что делать с содержимым (например, никогда не кэшировать это или предположить, что срок его действия истекает через 1 день и т. д.).

Также вы упомянули зашифрованные ответы. Кэш-серверы HTTP не могут кэшировать SSL, потому что для этого им потребуется расшифровывать страницы как «человеку посередине». Это было бы технически сложно (расшифровать страницу, сохранить ее и повторно зашифровать для клиента), а также нарушить безопасность страницы, вызывая предупреждения «недействительный сертификат» на стороне клиента. Технически возможно, чтобы кэш-сервер делал это, но это создает больше проблем, чем решает, и это плохая идея. Я сомневаюсь, что какие-либо кеш-серверы действительно делают такие вещи.

person SoapBox    schedule 11.01.2009

К сожалению, HTTP-кеширование основано на точных URI, и вы не можете добиться разумного поведения в вашем случае, не заставляя клиентов выполнять повторную проверку кэша.

Если у вас было:

GET /farm/123
POST /farm_update/123

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

Решение состоит в том, чтобы ускорить срок действия страниц и обрабатывать If-Modified-Since или E-Tag со статусом 304 Not Modified.

person Kornel    schedule 11.01.2009

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

person Karsten    schedule 11.01.2009

В ответе SoapBox:

  1. Я думаю, что If-Modified-Since — это двухэтапный GET, который я предложил в конце своего вопроса. Это похоже на нормальное решение, когда контент большой (т. е. когда стоимость удвоения количества запросов и, следовательно, накладные расходы компенсируются преимуществами отказа от повторной отправки контента. Это не так в моем примере с фермами, поскольку информация о каждой ферме коротка.)

  2. Совершенно разумно построить систему, которая отправляет зашифрованный контент по незашифрованному (HTTP) каналу. Представьте себе сценарий сервис-ориентированной архитектуры, в которой обновления происходят нечасто, а GET выполняются (а) часто, (б) должны быть чрезвычайно быстрыми и (в) должны быть зашифрованы. Вы должны создать сервер, который требует заголовок FROM (или, что то же самое, ключ API в параметрах запроса) и отправляет обратно асимметрично зашифрованную версию контента для запрашивающей стороны. Асимметричное шифрование работает медленно, но при правильном кэшировании превосходит комбинированное рукопожатие SSL (асимметричное шифрование) и симметричное шифрование содержимого. Добавление кеша перед этим сервером значительно ускорит GETs.

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

person Community    schedule 11.01.2009
comment
If-modified-since не увеличивает количество запросов. - person Kornel; 11.01.2009
comment
Я почти уверен, что это так. Если бы кэш мог определить, какие записи являются текущими, ему не пришлось бы отправлять запрос If-Modified-Since. Вы правы, это не удваивает число. Это зависит от соотношения операций чтения и записи. - person James A. Rosen; 11.01.2009
comment
If-Modified-Since не удваивает запросы — сервер просто отвечает либо ресурсом (если он изменился), либо ответом «Не изменено», для чего клиент должен использовать уже имеющуюся у него версию. - person Rowland Shaw; 11.01.2009
comment
Вы оба правы - это не удваивает число. Но в HTTP §13.2.1 ¶1 говорится, что кэширование HTTP работает лучше всего, когда кэши могут полностью избежать запросов к исходному серверу. Вот к чему я стремлюсь. - person James A. Rosen; 11.01.2009
comment
По мере того, как я углубляюсь, я все больше и больше вижу, что кэширование HTTP было построено с идеей кэширования, возвращающегося назад для проверки через If-Modified-Since. Это кажется большим количеством накладных расходов, но, похоже, это решает все мои проблемы. - person James A. Rosen; 11.01.2009
comment
Кэширующий сервер не может кэшировать получение https, поскольку канал SSL непрозрачен для сервера - на самом деле он даже не видит их как обычный HTTP, они выполняются с помощью метода CONNECT, который, по сути, пробивает соединение через сокет через прокси. - person frankodwyer; 11.01.2009
comment
(на самом деле я должен добавить, что есть некоторые коммерческие прокси, которые могут уродливо подделывать ЦС, чтобы обойти предупреждения сертификата SSL, но это действительно ужасное решение и требует, чтобы прокси-сервер рассматривался как доверенный ЦС) - person frankodwyer; 11.01.2009
comment
@frankodwyer - я всегда думал, что прокси могут видеть заголовки SSL-трафика. Я возьму шляпу в руки на № 3. Хорошие комментарии. - person James A. Rosen; 11.01.2009
comment
Мое личное мнение заключается в том, что любое банковское веб-приложение должно НЕ ничего кэшировать. Если это связано с деньгами, это критично, а если это банк, он должен позволить себе оборудование для обслуживания всех некэшированных запросов. - person Andrei Rînea; 10.05.2009