Суть веб приложения с широкими возможностями заключается в его динамичности. Неважно, насколько эффективно Ваше приложение, каждый запрос будет больше затрачивать ресурс, чем обслуживать статический файл.
Для многих веб приложений подобная ситуация в порядке вещей. Благодаря молниеносной быстроте Symfony2 Вы можете делать нечто действительно “полновесное”. При этом каждый запрос будет возвращаться быстро, не оказывая чрезмерной нагрузки на Ваш сервер.
Однако, по мере роста сайта, такой перерасход ресурса может стать проблемой. Обработка, которая, обычно, выполняется при каждом запросе, должна производиться только единожды. Именно для этого и предусмотрено кэширование.
Кэширование “на плечах гигантов”
Самый эффективный способ повысить производительность приложения – кэшировать все выходные данные страницы, а затем полностью блокировать приложение при каждом последующем запросе. Конечно же, такая схема не всегда возможна для динамичных сайтов. Но так ли это на самом деле? В этой главе мы покажем Вам как работает система кэширования Symfony2 и объясним, почему мы считаем такой способ лучшим.
Система кэширования Symfony2 полагается на простоту и мощь HTTP кэша, как это указывается в технических требованиях HTTP. Вместо повторного изобретения методологии кэширования Symfony2 пользуется стандартом, определяющим основные взаимодействия веба. Как только Вы поймёте основные модели кэширования HTTP (“валидация” и “завершение”), то будете готовы освоить систему кэширования Symfony2.
Для того, чтобы научиться кэшированию с помощью Symfony2, мы разобьём объяснение на четыре этапа:
Так как HTTP кэширование не является уникальным для Symfony, многие статьи можно найти в соответствующей теме. Если Вы только знакомитесь с HTTP кэшированием, мы настоятельно рекомендуем статью Раяна Томайко (Ryan Tomayko) “Что делает кэш” (Things Caches Do). Другим исчерпывающим источником является “Учебное Пособие по Кэшу” (Cache Tutorial) Марка Ноттингхэма (Mark Nottingham).
Использование шлюз-кэша
При использовании HTTP, кэш полностью отделён от Вашего приложения и находится между приложением и пользователем, делающим запрос.
Работа кэша заключается в том, чтобы принимать запросы запросы от пользователя и отправлять их приложению. Кэш также получает ответы приложения и перенаправляет их пользователю. Кэш выступает в роли посредника во взаимодействии “запрос-ответ” между пользователем и Вашим приложением.
Попутно кэш будет сохранять ответ, который будет расценен ка “пригодный для кэширования”. При повторном запросе к одному ресурсу, кэш отправляет сохранённый ответ пользователю, полностью игнорируя приложение.
Данный тип известен как HTTP шлюз-кэш.
Типы кэша
Однако шлюз-кэш не является единственным типом кэша. Фактически, заголовки HTTP кэша, отправленные Вашим приложением, поглощаются и интерпретируются в три различных типа кэша:
Шлюз-кэш иногда может выступать в роли резервного прокси- или замещающего кэша, или даже HTTP ускорителя.
Разница между приватным кэшем и кэшем совместного доступа становится более очевидной, когда мы говорим о кэшировании ответов с содержанием, свойственным только одному пользователю (например, информация учётной записи).
Кажды ответ из Вашего приложения, вероятно будет проходить через один или оба первые типы кэша. Эти типы кэша являются внешними и не контролируются Вами, однако они следуют в направлении HTTP кэша, указанном в ответе.
Реверсный прокси-сервер Symfony2
Symfony2 изначально содержит реверсный прокси-сервер (называемый также шлюз-кэшем), написанный на PHP. Включите его и соответствующие ответы Вашего приложения тут же начнут кэшироваться. Установка довольно проста. Каждое новое приложение Symfony2 содержит предварительно настроенное ядро кэширования (AppCache), которое заменяет собой использующееся по умолчанию (AppKernel). Ядро кэширования и является реверсным прокси-сервером.
Чтобы включить кэширование, измените код во фронт-контроллере:
// web/app.php
require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';
require_once __DIR__.'/../app/AppCache.php';
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
// wrap the default AppKernel with the AppCache one
$kernel = new AppCache($kernel);
$kernel->handle(Request::createFromGlobals())->send();
Ядро кэширования немедленно начнёт действовать в качестве реверсного прокси-сервера, кэшируя ответы Вашего приложения и возвращая их пользователю.
Ядро содержит специальную функцию getLog(), которая отображает строковое отображение происходящего в слое кэша. Эта функция может быть использована в среде программирования для отладки и проверки поведения кэша:
error_log($kernel->getLog());
Объект AppCache имеет удобные настройки, однако Вы можете настроить объект с помощью набора опций, устанавливаемого путём подмены функции getOptions():
// app/AppCache.php
class AppCache extends Cache
{
protected function getOptions()
{
return array(
'debug' => false,
'default_ttl' => 0,
'private_headers' => array('Authorization', 'Cookie'),
'allow_reload' => false,
'allow_revalidate' => false,
'stale_while_revalidate' => 2,
'stale_if_error' => 60,
);
}
}
Без подмены функции getOptions(), опция отладки автоматически будет использоваться для заменённого AppKernel.
Вот список основных опций:
Если для debug установлено значение true, Symfony2 автоматически добавляет заголовок X-Symfony-Cache к ответу, содержащему полезную информацию о совпадениях и несовпадениях кэша.
Переход с реверсного прокси-сервера на другой
Реверсный прокси-сервер Symfony2 – это прекрасное средство для использования при программировании Вашего сайта или при перемещении его на хост совместного доступа, где Вы не можете установить ничего, помимо написанного с использованием PHP кода. Однако, написанное на PHP не может сравниться в скорости с прокси, написанным на C. Поэтому мы настоятельно рекомендуем Вам, по возможности, использовать Varnish или Squid на производственных серверах. Хорошая новость заключается в том, что переключение с одного прокси сервера на другой осуществляется просто, поскольку не требует изменения кода Вашего приложения. Для начала используйте простой реверсный прокси-сервер Symfony2, а затем, когда возрастёт трафик, Вы сможете перейти на Varnish.
Эффективность реверсного прокси-сервера Symfony2 не зависит от сложности приложения. Это потому, что ядро приложения загружается только тогда, когда к нему должен быть направлен запрос.
Введение в HTTP кэширование
Чтобы воспользоваться доступными уровнями кэша, Вашему приложению необходимо уведомлять о том, какие ответы пригодны для кэширования, а также правила, определяющие, когда и как кэш становится неактуальным. Это делается путём определения в ответе HTTP заголовков кэша.
Обратите внимание на то, что “HTTP” – это язык (простой текстовый язык), используемый веб-клиентами (нпример, браузерами) и веб-серверами для взаимодействия друг с другом. Когда мы говорим о HTTP кэшировании, мы имеем в виду часть этого языка, которая позволяет пользователям и серверам обмениваться информацией, пригодной для кэширования.
HTTP определяет четыре заголовка кэша, которые нас интересуют:
Наиболее важным и универсальным заголовком является Cache-Control, который является, по сути, набором различной кэш-информации.
Заголовок Cache-Control
Заголовок Cache-Control уникален, поскольку содержит не один, но несколько блоков информации о пригодности ответа для кэширования. Каждый информационный блок выделен запятой:
Cache-Control: private, max-age=0, must-revalidate Cache-Control: max-age=3600, must-revalidate
В Symfony Cache-Control имеет собственное место, дабы сделать создание этого заголовка более управляемым:
$response = new Response();
// mark the response as either public or private
$response->setPublic();
$response->setPrivate();
// set the private or shared max age
$response->setMaxAge(600);
$response->setSharedMaxAge(600);
// set a custom Cache-Control directive
$response->headers->addCacheControlDirective('must-revalidate', true);
Ответы Public и Private
Два типа кэша, прокси- и шлюз-кэш, являются “общедоступными”, поскольку их содержимое доступно более чем одному пользователю. Даже если ответ, предназначенный для определённого пользователя, будет ошибочно сохранён кэшем совместного доступа, позже его можно будет вернуть любому количеству различных пользователей. Представьте, что информация Вашей учётной записи была кэширована и затем выводится для каждого следующего пользователя, запросившего страницу собственной учётной записи!
Чтобы справиться с подобной ситуацией, для каждого ответа можно установить статус public или private:
По умолчанию, Symfony делает каждый ответ приватным. Чтобы воспользоваться кэшем совместного доступа (например, реверсным прокси-сервером Symfony2), необходимо установить для ответа статус public.
Безопасные функции
HTTP кэширование работает только для “безопасных” функций (например, GET или HEAD). Понятие “безопасные” означает, что Вы никогда не меняете статус приложения на сервере во время обработки запроса (естественно, Вы можете вести учёт информации, кэшировать данные и т.д.). Из вышесказанного можно сделать два разумных вывода:
Правила кэширования и значения по умолчанию
HTTP 1.1 позволяет кэшировать любую информацию по умолчанию до тех пор, пока не будет изменён заголовок Cache-Control. Фактически, большинство типов кэша бездействуют, если запросы имеют cookie, авторизационный заголовок, не используют “безопасные” функции (т.е. PUT, POST, DELETE), или в тех случаях, когда ответы содержат код изменения статуса.
Если заголовок не выбран разработчиком, Symfony2 автоматически определяет стандартный заголовок Cache-Control для соблюдения следующих правил:
HTTP “завершение” и валидация
Спецификация HTTP подразделяется на две модели кэширования:
Назначение обеих моделей сходно: полагаясь на кэш для сохранения и отображения “свежих” ответов, не допустить повторного генерирования одного и того же ответа.
Чтение HTTP спецификации
HTTP спецификация определяет простой, но мощный язык для взаимодействия между пользователями и серверами. В качестве веб-программиста нашу работу контролирует модель “запрос-ответ”. К сожалению, настоящий спецификационный документ – RFC 2616 – бывает сложно прочесть.
Для переписывания RFC 2616 ведётся непрекращающаяся работа (HTTP Bis). Она ведётся не для описания новой версии HTTP, но, в основном, объясняет оригинальную спецификацию HTTP. Структура также улучшена, поскольку спецификация разбита на семь частей; всё, что касается HTTP кэширования можно найти в двух соответствующих частях (P4 – Условные запросы и P6 – кэширование: браузер- и посреднический кэш).
Как веб-разработчики, мы настоятельно рекомендуем Вам читать спецификацию. Она доступна для понимания и достаточно мощна – десять лет спустя после создания, она по-прежнему ценится. Не смотрите на внешний вид спецификации – её содержание гораздо ценнее обложки.
Истечение срока пригодности
“Завершение” является наиболее эффективной и прямо направленной из двух моделей кэширования; её необходимо использовать везде, где возможно. Если ответ кэшируется с помощью “завершения”, кэш сохранит его и отобразит напрямую, без обращения к приложению, до тех пор, пока не истечёт срок актуальности ответа.
“Завершение” может быть выполнено с помощью двух, почти идентичных, HTTP заголовков: Expires или Cache-Control.
“Завершение” с помощью заголовка Expires
Согласно HTTP спецификации, “поле заголовка Expires отображает дату/время, после которой ответ будет считаться неактуальным.” Заголовок Expires можно установить с помощью функции setExpires() Response. В качестве параметра потребуется привязка DateTime:
$date = new DateTime();
$date->modify('+600 seconds');
$response->setExpires($date);
В результате, HTTP заголовок будет выглядеть следующим образом:
Expires: Thu, 01 Mar 2011 16:00:00 GMT
Функция setExpires() автоматически конвертирует дату, согласно часовому поясу GMT, которого требует спецификация.
Однако, для заголовка Expires существуют два ограничения. Во-первых, часы веб-сервера и кэша (например, браузера) должны быть синхронизированы. Во-вторых, в спецификации говорится: “HTTP/1.1 сервера не должны отправлять даты Expires более, чем на год вперёд.”
“Завершение” с помощью заголовка Cache-Control
Из-за ограничения, налагаемых на заголовок Expires, большую часть времени вместо него Вы будете использовать заголовок Cache-Control. Напомним, что Cache-Control используется для определения множества различных директив кэша. Для “завершения” существуют две директивы: max-age и s-maxage. Первая используется для всех типов кэша, вторая же учитывается типами кэша совместного доступа:
// Sets the number of seconds after which the response // should no longer be considered fresh $response->setMaxAge(600); // Same as above but only for shared caches $response->setSharedMaxAge(600);
Заголовок Cache-Control приобретёт следующий формат (он может иметь дополнительные директивы):
Cache-Control: max-age=600, s-maxage=600
Валидация
В случаях, когда необходимо обновить ресурс непосредственно после изменения базовых данных, модель “завершения” неэффективна. При использовании указанной модели к приложению не будет отправляться запрос об отображении обновлённого ответа до тех пор, пока срок актуальности кэша не истечёт окончательно.
Валидационная модель направлена на разрешение этого вопроса. При использовании этой модели кэш продолжает хранить ответы. Разница заключается в том, что при каждом запросе кэш обращается к приложению за подтверждением валидности ответа. Если кэш всё ещё актуален, Ваше приложение выдаст статусный код 304 без содержания. Для кэша это служит сигналом о выдаче хранящегося в нём ответа.
Используя валидационную модель, Вы, в основном, экономите пропускную способность, поскольку отображение не отсылается одному клиенту дважды (вместо этого выдаётся ответ 304). Однако, если Вы хорошо продумали Ваше приложение, можно использовать минимум данных для выдачи ответа 304, а также сэкономить ресурс CPU (ниже приведён пример реализации).
Статусный код 304 означает: “Без Изменений” («Not Modified»). Этот код важен, поскольку в ответ на запрос не выдаётся никакого настоящего содержания. Вместо этого выдаётся “легковесный” набор директив, указывающих кэшу на необходимость использования сохранённой в нём версии.
Как и в случае с “завершением”, для валидационной модели существуют два различных HTTP заголовка: ETag и Last-Modified.
Валидация с помощью заголовка Etag
Заголовок ETag является строчным (который называют “объектным тэгом” («entity-tag»)), который однозначно идентифицирует отображение целевого ресурса. Он полностью генерируется и устанавливается Вашим приложением, поэтому если, к примеру, ресурс /about хранящийся в кэш, будет актуальным, то он и отобразится Вашим приложением. ETag является, по сути, отпечатком пальца и используется для быстрого сравнения двух различных версий ресурса, чтобы удостовериться в их сходстве. Как и отпечатки пальцев, каждый ETag должен быть уникальным при каждом отображении одного и того же ресурса.
Давайте рассмотрим пример простого применения генерирования ETag в качестве md5 содержания:
public function indexAction()
{
$response = $this->render('MyBundle:Main:index.html.twig');
$response->setETag(md5($response->getContent()));
$response->isNotModified($this->getRequest());
return $response;
}
Функция Response::isNotModified() сравнивает Etag, отправленный запросом (Request) с одним значением, для ответа (Response). Если оба совпадают, функция автоматически устанавливает статусный код 304 для Response.
Данный алгоритм достаточно прост и очень обобщён. Однако, Вам необходимо будет создать Response целиком перед тем, как сделать выкладку ETag. Иными словами, алгоритм экономит пропускную способность, но не циклы процессора.
В разделе “Оптимизация Вашего кода с помощью валидации” мы покажем, как можно, без особых забот, использовать валидацию для определения актуальности кэша.
Symfony2 также поддерживает редуцированные Etags путём добавления true в качестве второго параметра для функции setETag().
Валидация с помощью заголовка Last-Modified
Заголовок Last-Modified представляет собой вторую форму валидации. Согласно HTTP спецификации: “Поле заголовка Last-Modified отображает дату и время, когда исходный сервер полагает, что отображение было обновлено.” Иными словами, приложение определяет, было ли обновлено содержимое кэша, основываясь на информации о его обновлении с того времени, как ответ был закеширован.
Например, Вы можете использовать данные последнего обновления для всех объектов, необходимых для того, чтобы сделать выборку отображения ресурса, в качестве значения для заголовка Last-Modified:
public function showAction($articleSlug)
{
// ...
$articleDate = new \DateTime($article->getUpdatedAt());
$authorDate = new \DateTime($author->getUpdatedAt());
$date = $authorDate > $articleDate ? $authorDate : $articleDate;
$response->setLastModified($date);
$response->isNotModified($this->getRequest());
return $response;
}
Функция Response::isNotModified() сравнивает отправленный с запросом заголовок If-Modified-Since с заголовком Last-Modified. Если есть сходство, статусный код 304 будет установлен для Response.
Заголовок запроса If-Modified-Since эквивалентен заголовку Last-Modified последнего ответа, отправленного пользователю. Таким образом взаимодействуют между собой пользователь и сервер, а также определяют: обновлён ли кэшированный ресурс или нет.
Оптимизация кода с помощью валидации
Основная цель любой стратегии кэширования – облегчить загрузку приложения. Иными словами, чем меньше Вы вызываете ответов 304 в приложении, тем лучше. Именно этим и занимается функция Response::isNotModified(), предлагая простую и эффективную структуру:
public function showAction($articleSlug)
{
// Get the minimum information to compute
// the ETag or the Last-Modified value
// (based on the Request, data are retrieved from
// a database or a key-value store for instance)
$article = // ...
// create a Response with a ETag and/or a Last-Modified header
$response = new Response();
$response->setETag($article->computeETag());
$response->setLastModified($article->getPublishedAt());
// Check that the Response is not modified for the given Request
if ($response->isNotModified($this->getRequest())) {
// return the 304 Response immediately
return $response;
} else {
// do more work here - like retrieving more data
$comments = // ...
// or render a template with the $response you've already started
return $this->render(
'MyBundle:MyController:article.html.twig',
array('article' => $article, 'comments' => $comments),
$response
);
}
}
Если Response не изменяется, isNotModified() автоматически вставляет ответный код статуса 304, удаляет содержание, а также некоторые заголовки, которых не должно быть при указанном коде статуса.
Изменение ответа
Итак, мы пришли к выводу, что каждый URL является точным отображением целевого ресурса. По умолчанию, HTTP кэширование производится с использованием URL ресурса в качестве ключа кэша. Если два человека запрашивают один и тот же URL ресурса, который будет сохранён в кэше, второй человек получит кэшированную версию.
Иногда этого недостаточно и разные варианты одного и того же URL необходимо будет кэшировать, основываясь на одном или более значениях заголовка запроса. Например, когда Вы уплотняете страницы, если клиент это поддерживает, любой заданный URL имеет два варианта отображения: один – когда клиент поддерживает уплотнение, другой – не поддерживает. Данное определение производится с помощью значения заголовка запроса Accept-Encoding.
В таком случае, нам нужно, чтобы кэш сохранил оба варианта ответа для определённого URL и выдать их, основываясь на значении запроса Accept-Encoding. Для этого используется заголовок ответа Vary, представляющий из себя разделённый запятыми список заголовков, значения которых приводят в действие различные варианты отображения запрашиваемого ресурса:
Vary: Accept-Encoding, User-Agent
Заголовок Vary будет кэшировать различные варианты каждого ресурса, основываясь на URL значениях заголовков запросов Accept-Encoding и User-Agent.
Объект Response предлагает понятный интерфейс для управления заголовком Vary:
// set one vary header
$response->setVary('Accept-Encoding');
// set multiple vary headers
$response->setVary(array('Accept-Encoding', 'User-Agent'));
Функция setVary() содержит название заголовка или же целый для которых происходит изменение ответа.
“Завершение” и валидация
Естественно, Вы можете пользоваться двумя моделями в пределах одного Response. Поскольку “завершение” превосходит валидацию, Вы легко можете извлечь выгоду из лучшей, или из обеих моделей. Иными словами, используя “завершение” и валидацию, Вы можете проинструктировать кэш в обработке хранящегося в нём содержания, совершая перепроверку через определённый интервал (“завершение”), чтобы убедиться в валидности содержания.
Другие функции ответа
Класс Response предлагает гораздо больше функций, подобных кэшу. Вот наиболее полезные из них:
// Marks the Response stale $response->expire(); // Force the response to return a proper 304 response with no content $response->setNotModified();
К тому же, большинство сходных с кэшем HTTP заголовков можно установить с помощью единственной функции setCache():
// Set cache settings in one call $response->setCache(array( 'etag' => $etag, 'last_modified' => $date, 'max_age' => 10, 's_maxage' => 10, 'public' => true, // 'private' => true, ));
Использование интеграции с обратным прокси-сервером (ESI)
Шлюз-кэш – это отличный способ повысить производительность Вашего сайта. Но у него имеется одно ограничение: шлюз-кэш кэширует только страницы целиком. Если не получается кэшировать страницу целиком, или она содержит динамические составляющие – Вам не повезло. К счастью, Symfony2 предлагает решение для таких случаев, основанное на технологии, названной ESI, или Интеграцией с Обратным Прокси-Сервером (Edge Side Includes). Akamaï написал эту спецификацию почти 10 лет назад и она позволяет использовать различную стратегию кэширования для для различных же компонентов страницы.
Спецификация ESI описывает тэги, которые Вы можете вставить в страницы для взаимодействия со шлюз-кэшем. Только один таг, include, применяется в Symfony2, поскольку он является единственным полезным тэгом вне контекста Akamaï:
<html> <body> Some content <!-- Embed the content of another page here --> <esi:include src="http://..." /> More content </body> </html>
Из примера видно, что каждый ESI тэг соответствует определённому URL. ESI тэг отображает фрагмент страницы, который можно выбрать с помощью заданного URL.
При выполнении запроса шлюз-кэш выбирает из кэша всю страницу целиком или запрашивает её из выходного буфера. Если ответ содержит один или более ESI тэгов, они запускаются подобным путём. Иными словами, шлюз-кэш получает встроенные фрагменты страницы либо из кэша последней, либо из выходного буфера. Когда все ESI тэги задействованы, шлюз-кэш объединяет их в основную страницу и отправляет конечное содержание пользователю.
Всё вышеперечисленное происходит на уровне шлюз-кэша (т.е. вне Вашего приложения). Как Вы убедитесь, выбрав ESI тэги, Symfony2 делает их внедрение лёгким, не требующим, практически, никаких усилий.
Использование ESI в Symfony2
Чтобы использовать ESI, убедитесь, что включили его в настройки приложения:
YAML:
# app/config/config.yml
framework:
# ...
esi: { enabled: true }
XML:
<!-- app/config/config.xml --> <framework:config ...> <!-- ... --> <framework:esi enabled="true" /> </framework:config>
PHP:
// app/config/config.php
$container->loadFromExtension('framework', array(
// ...
'esi' => array('enabled' => true),
));
Предположим, у нас есть страница, которая относительно статична, исключая новостной код внизу содержания. С помощью ESI мы можем кэшировать новостной код независимо остальных ресурсов страницы.
public function indexAction()
{
$response = $this->render('MyBundle:MyController:index.html.twig');
$response->setSharedMaxAge(600);
return $response;
}
В следующем примере мы установили 10-минутный жизненный цикл для полностраничного кэша. А теперь давайте включим новостной код в шаблон путём вложения действия. Это можно сделать с помощью хелпера render.
Поскольку вложенное содержание исходит из другой страницы, (или контроллера), Symfony2 использует стандартный хелпер render для настройки ESI тэгов:
Twig:
{% render '...:news' with {}, {'standalone': true} %}
PHP:
<?php echo $view['actions']->render('...:news', array(), array('standalone' => true)) ?>
Устанавливая standalone на true, Вы сообщаете Symfony2, что действие должно визуализироваться в качестве ESI тэга. Вам может стать интересно: зачем использовать хелпер вместо того, чтобы прописать ESI тэг собственноручно? Дело в том, что использование хелпера позволяет приложению работать даже без установки шлюз-кэша. Давайте рассмотрим как это работает.
Когда standalone установлен на false (по умолчанию), Symfony2 объединяет всё включённое содержание страницы перед тем, как отправить ответ пользователю. Но, если standalone установлен на true, и если Symfony2 определяет взаимодействие со шлюз-кэшем, поддерживающим ESI, генерируется ESI тэг присоединения. Но если шлюз-кэш отсутствует или он не поддерживает ESI, Symfony2 объединить содержание страницы так же, как это происходило бы при условии, что standalone установлен на false.
Symfony2 определяет поддержку ESI шлюз-кэшем с помощью другой спецификации Akamaï, которая поддерживается реверсным прокси-сервером Symfony2.
Теперь вложенное действие может определять собственные правила кэширования, независимо от главной страницы.
public function newsAction()
{
// ...
$response->setSharedMaxAge(60);
}
С помощью ESI полностраничный кэш будет валидным в течение 600 секунд, однако новостные компоненты кэша будут существовать только 60 секунд.
Однако, требование ESI таково, что вложенное действие будет доступно посредством URL, чтобы шлюз-кэш смог выбрать его независимо от другого содержания страницы. Естественно, действие не может быть доступно посредством URL до тех пор, пока на него указывает определённый путь. Symfony2 позаботится об этом с помощью универсальных пути и контроллера. Для того, чтобы ESI тэг включения функционировал должным образом, Вам необходимо указать путь _internal:
YAML:
# app/config/routing.yml _internal: resource: "@FrameworkBundle/Resources/config/routing/internal.xml" prefix: /_internal
XML:
<!-- app/config/routing.xml --> <?xml version="1.0" encoding="UTF-8" ?> <routes xmlns="http://symfony.com/schema/routing" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> <import resource="@FrameworkBundle/Resources/config/routing/internal.xml" prefix="/_internal" /> </routes>
PHP:
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection->addCollection($loader->import('@FrameworkBundle/Resources/config/routing/internal.xml', '/_internal'));
return $collection;
Поскольку указанный путь позволяет сделать все действия доступными посредством URL, Вы могли бы пожелать защитить его, используя Symfony2 firewall (путём предоставления доступа к IP диапазону Вашего реверсного прокси-сервера).
Большое преимущество данной стратегии кэширования заключается в том, что Вы можете сделать приложение динамичным и, в то же время, обращаться к нему как можно реже.
Как только Вы начали использовать ESI, помните, что следует постоянно использовать директиву s-maxage вместо max-age. Поскольку браузер только иногда получает агрегированные данные, он не уведомляется о добавочных компонентах и поэтому будет подчиняться директиве max-age, кэшируя страницу целиком. А вам это не нужно.
Хелпер render поддерживает ещё две полезные опции:
Аннулирование кэша
“В компьтерной науке всего две трудности – аннулирование кэша и присваивание имён.” – Фил Карлтон (Phil Karlton)
Вам не понадобится аннулировать кэшированные данные, поскольку аннулирование предусмотрено в моделях HTTP кэша. Если Вы используете валидацию, Вам не придётся что-либо аннулировать по определению. Если же Вы используете “завершение” и Вам необходимо аннулировать ресурс, это означает, что установленная дата истечения срока действия слишком велика.
Вдобавок, не существует механизма аннулирования, использующего какой-либо прокси-сервер без изменений в коде Вашего приложения.
Фактически, все реверсные прокси-сервера предоставляют способы удалить кэшированные данные, однако, следует избегать их насколько это возможно. Самым стандартным способом является очистка кэша для заданного URL путём запрашивания его с помощью особой HTTP функции PURGE.
Таким вот образом Вы можете настроить реверсный прокси-сервер Symfony2 для поддержки HTTP функции PURGE:
// app/AppCache.php
class AppCache extends Cache
{
protected function invalidate(Request $request)
{
if ('PURGE' !== $request->getMethod()) {
return parent::invalidate($request);
}
$response = new Response();
if (!$this->store->purge($request->getUri())) {
$response->setStatusCode(404, 'Not purged');
} else {
$response->setStatusCode(200, 'Purged');
}
return $response;
}
}
Вам необходимо каким-либо защитить способом функцию PURGE от несанкционированного использования другими людьми.
Резюме
Symfony2 разработано для соблюдения проверенного свода правил – HTTP. Кэширование не является исключением. Освоить систему кэширования Symfony2 значит: ознакомиться и эффективно использовать модели HTTP кэша. Иными словами, вместо того, чтобы полагаться только на документацию и пример кода Symfony2, у Вас есть доступ к миру знаний, сходным с HTTP кэшированием и шлюз-кэшами, такими как Varnish.