Правильне http-кешування httplib2

Вам неодмінно потрібно розуміти, як працює HTTP-кешування. Чесне слово, потрібно. Я багато раз згадував, що вибір HTTP-методів вкрай важливий при проектуванні веб-сервісу, - зокрема, вибір методу GET дозволяє отримати виграш від використання HTTP-кешування. Так ось: для того, щоб отримати від використання методу GET все ті переваги, які він здатний забезпечити, потрібно розуміти, як HTTP-кешування працює, і як можна, ефективно його задіявши, збільшити продуктивність вашого сервісу.

У цій статті я не буду пояснювати, як налаштувати кешування в тому веб-сервері, який ви використовуєте, і не буду описувати різні види кешування. Якщо вам все це цікаво, то рекомендую ознайомитися з чудовим підручником по HTTP-кешування від Марка Ноттінгема.

Найперше, що вам потрібно розуміти - які цілі переслідуються моделлю кешування, використовуваної в HTTP. Одна з таких цілей - це дозволити і клієнту, і сервера задавати умови, в яких передається документ може братися з кеша. Легко бачити, що одне це вже привносить в модель кешування певну складність.

Основу моделі кешування, використовуваної в HTTP, утворюють валідатори - частини запиту, які використовуються клієнтом для того, щоб переконатися, що кешированний документ все ще дійсний (тобто не застарів). Використання валідаторів дозволяє клієнту або проміжного сервера перевіряти поточний стан документа, не передаючи на сервер його кешовану копію цілком. Сервер ж, в свою чергу, передає у відповіді документ тільки в тому випадку, якщо отриманий ним валідатор свідчить про наявність в кеші клієнта застарілої (недійсною) копії.

валідатори

Один з валідаторів, використовуваних в HTTP - це ETag. ETag є хеш ( «відбиток») байтів документа: якщо в документі зміниться хоч один байт, то зміниться і ETag.

Перед тим, як використовувати цей валідатор, потрібно хоч раз запросити документ методом GET. якщо сервер підтримує цю можливість, то заголовок ETag в його відповіді буде містити хеш передається версії документа. Клієнт в цьому випадку зберігає в кеші разом з самим документом його ETag. і в подальших запитах того ж документа використовує збережений ETag як валідатора.

Наприклад, нехай я запросив документ з сервера example.org. і отримав таку відповідь:

Тоді наступного разу, коли я звертаюсь цей же документ методом GET. я зможу використовувати валідатор. Зверніть увагу: збережене значення ETag передається в заголовку If-None-Match.

Якщо за час між запитами документ не змінювався, то сервер поверне код 304 Not Modified.

Якщо ж документ змінювався, то сервер поверне код 200 і передасть у відповіді нову версію документа, а також значення ETag цієї нової версії.

Cache-Control

Валідатори використовуються для перевірки того, чи не змінився документ; для того, щоб управляти збереженням його копії в кеші, використовується заголовок Cache-Control. Найголовніша з директив управління кешем - це max-age. вона вказує, що збережена в кеші копія документа застаріває через max-age секунд. Ця директива може використовуватися як в запиті, так і у відповіді, - так що і клієнт, і сервер можуть вирішувати, скільки часу будуть дійсні передані ними документи. Якщо на сервері є досить новий кешированний відповідь, то його можна повернути прямо з кешу; інакше ж відбувається описана вище процедура валідації.

Розглянемо ще раз отриманий нами від сервера відповідь. У ньому заголовок Cache-Control задає max-age = 7200. тобто кешована копія документа перестає бути дійсною через 2 години.

Крім max-age. в заголовку Cache-Control допускається безліч інших директив. Кожна з них може використовуватися або в запитах, або в відповідях, або - як max-age - і в запитах, і у відповідях.

Директиви, які допускаються в запитах

Те ж, що must-revalidate. але діє тільки на проксі-сервера.

Розглянемо приклади використання заголовка Cache-Control:

Cache-Control: private, max-age = 3600

Припустимо у відповіді від сервера; означає, що відповідь може зберігатися тільки в закритому кеші протягом години.

Cache-Control: public, must-revalidate, max-age = 7200

Чи означає, що відповідь може зберігатися у відкритому кеші протягом двох годин; після їх закінчення дійсність збереженої копії повинна перевірятися при кожному запиті.

Cache-Control: must-revalidate, max-age = 0

Змушує клієнта перевіряти дійсність збереженої копії при кожному запиті; max-age = 0 вказує, що документ застаріває відразу ж після його отримання клієнтом. У замітці Марка Ноттінгема «Leveraging the Web: Caching» наведено цікавий приклад випадку, в якому можна використовувати такий заголовок.

Практично те ж саме, що і в попередньому прикладі; єдина відмінність - що клієнт міг використовувати в запиті директиву max-stale і отримати застарілий відповідь, тоді як директива must-revalidate скасувала б дію max-stale. Така складна система необхідна тому, що, як я зазначав раніше, і сервер, і клієнт можуть одночасно впливати на використання кешу.

Всі ці приклади використання заголовка Cache-Control розглядають його застосування у відповіді від сервера. Тепер розглянемо приклади, коли цей заголовок використовується в запиті.

Викликає «повне» ( «end-to-end») оновлення документа: кеш, до якого звертається клієнт, повинен знову запросити документ у сервера, з якого він був отриманий.

Таким заголовком клієнт запитує документ, який буде залишатися дійсним протягом 200 секунд після запиту.

Напевно, вам цікаво, чи не заплутується кеш у всіх цих ситуаціях. Наприклад, що він повинен робити, якщо сервер може повертати різні документи для одного і того ж URI? Для таких випадків в HTTP передбачений заголовок Vary. Цей заголовок вказує кешу назви всіх тих заголовків запиту, при зміні яких сервер повернув би інший документ.

Припустимо, що сервер погоджує з клієнтом тип повертається документа - тоді різні відповіді для одного і того ж URI могли б мати різний заголовок Content-Type. в залежності від узгодженого типу документа. Тоді сервер може додати у відповідь заголовок Vary: accept. і кеш зберігав би окремо відповіді на запити з різними значеннями заголовка Accept.

У цьому прикладі сервер вказує, що відповідь можна зберігати в кеші протягом двох годин, причому окремо для запитів з різними значеннями заголовків Accept-Encoding і User-Agent.

Connection

Після того, як сервер успішно перевірить дійсність кешованого результату, наприклад за допомогою валідатора в заголовку If-None-Match. - він повертає код 304 Not Modified. Більше в цьому випадку нічого не відбувається, так? Насправді, не зовсім: сервер може послати разом з відповіддю 304 Not Modified значення заголовків документа, щоб оновити їх в кеші. Крім цього, сервер може в заголовку Connection перерахувати назви тих заголовків, які не треба оновлювати в кеші.

За замовчуванням, деякі заголовки не зберігаються в кеші - це так звані «заголовки з'єднання» ( «hop-by-hop»): Connection, Keep-Alive, Proxy-Authenticate, Proxy-Authorization, TE, Trailers, Transfer-Encoding і Upgrade . Значення всіх інших заголовків - «заголовків документа» ( «end-to-end») - за замовчуванням кешуються.

У цьому прикладі значення заголовка Date. яка не є заголовком з'єднання і не вказаного в заголовку Connection. буде збережено в кеші.

Якщо б тільки все було так просто

Хоча всі описані механізми кешування представляють певну складність для сприйняття, принаймні вони утворюють струнку логічну систему. Природно, все ускладнюється необхідністю роботи з існуючими серверами і клієнтами, які використовують протокол HTTP 1.0. У цьому більш старому протоколі для управління кешуванням використовуються інші заголовки - оперують часом; всі ці старіші заголовки підтримуються в HTTP 1.1 для забезпечення сумісності.

Використовувана в HTTP 1.0 модель кешування побудована навколо часу старіння документа. Валідатор Last-Modified перевіряє час останньої зміни документа. Кеш використовує для перевірки дійсності документа заголовки Date. Expires. Last-Modified і If-Modified-Since.

Тепер, коли ви розумієте, як працює кешування в HTTP, вам напевно цікаво, чи немає для вашого улюбленого мови готових бібліотек з підтримкою кешування. Я можу відповісти про Python: на жаль, поки що немає. Мені дуже прикро, що жодна з кращих реалізацій HTTP-клієнтів не написана на моєму улюбленому мовою. І потрібно це непорозуміння виправити.

Я радий представити вам бібліотеку httplib2 - повнофункціональний HTTP-клієнт, написаний на Python. Ця бібліотека підтримує локальний закритий кеш і розуміє всі описані в цій статті операції над ним. Крім цього, в ній є багато можливостей, зазвичай відсутніх в інших HTTP-бібліотеках:

Про решту можливостей ви можете дізнатися на сторінці проекту httplib2.

Наступного разу