Русская документация Symfony2 на SymfonyGuru
Дата последнего обновления: 2012-05-20.
Дата последнего обновления: 2012-05-20.
Природа насыщенных (богатых) веб-приложений подразумевает, что они динамические. Вне зависимости от того, насколько эффективно ваше приложение, каждый запрос будет содержать работы больше чем отдача простого статического файла.
И для большинства веб-приложений это вполне нормально. Symfony2 очень быстр и, если вы не делаете чего-то действительно тяжеловесного, каждый запрос будет обрабатываться быстро и не создавая стрессовых ситуаций на сервере.
Но, по мере роста вашего сайта, рост нагрузки может стать проблемой. Работа, которая обычно выполняется для каждого запроса, теперь должна быть выполнена только единожды. И это именно то, чего позволяет добиться кэширование.
Наиболее эффективным способом увеличить быстродействие приложения является кэширование страницы целиком и затем, в обход приложения, отдавать кэшированные данные для каждого запроса. Конечно же, это не всегда возможно применить, особенно для очень динамично меняющихся сайтов... или всё же возможно? В этой главе вы увидите, как работает система кэширования Symfony2 и почему мы считаем это наилучшим решением из возможных.
Система кэширования Symfony2 отличается от других, так как она полагается на простоту и мощь HTTP кэширования, как это определено в спецификации HTTP (см. Спецификация протокола HTTP). Вместо того чтобы изобретать кэширование заново, Symfony2 пользуется стандартом, который определяет базовые коммуникации в Web. Как только вы поймёте основополагающие модели HTTP валидации и истечения срока для кэша, вы будете готовы к управлению системой кэширования Symfony2.
С целью изучения того, как кэшировать в Symfony2, мы пройдём четыре шага:
Так как HTTP кэширование не является достоянием лишь Symfony, существует множество статей по данной теме. Если вы новичок в HTTP кэшировании, мы настоятельно рекомендуем вам прочитать статью Ryan Tomayko: Things Caches Do. Другим исчерпывающим руководством является Cache Tutorial от Mark Nottingham.
При кэшировании при помощи HTTP, кэш полностью отделён от вашего приложения и располагается между вашим приложением и клиентом, выполнившем запрос.
Работа кэша заключается в приёме запроса от клиента и передаче его вашему приложению. Кэш также будет получать ответ от вашего приложения и перенаправлять его далее к клиенту. Кэш является посредником в клиент-серверных коммуникациях между клиентом и вашим приложением.
По пути, кэш будет сохранять каждый ответ, который полагает “кэшируемым” (см. Введение в HTTP кэширование). Если этот же ресурс будет запрошен ещё раз, кэш отправит сохранённый (кэшированный) ответ клиенту, игнорируя ваше приложение.
Этот тип кэширования известен под именем “кэширующего HTTP шлюза”. Существует много кэшеров такого типа, например: Varnish, Squid в режиме обратного прокси, а также обратный прокси Symfony2.
Но кэширующим шлюзом типы кэшеров не исчерпываются. Фактически, заголовки HTTP кэша, отправляемые вашим приложением, могут быть получены и использованы тремя различными типами кэшеров:
Совет
Кэширующие шлюзы иногда называют кэширующими обратными прокси, суррогатными кэшерами и даже HTTP акселераторами.
Примечание
Значимость личного кэша по сравнению с кэшем общего доступа становится более заметной, если мы говорим о кэшировании ответов, содержащих контент, относящийся к конкретному пользователю (например, информация о счёте).
Каждый ответ от вашего приложения будет проходить через первый тип кэша или же через оба - первый и второй. Эти кэши находятся вне вашего контроля, но следуют указаниям для HTTP кэша, которые есть в ответе.
Symfony2 содержит обратный прокси (также называемый кэширующим шлюзом), написанный на PHP. Активируйте его и кэшируемые ответы вашего приложения начнут кэшироваться надлежащим образом. Его установка очень проста. Каждое новое приложение Symfony2 содержит уже настроенное кэширующее ядро (AppCache), которое служит оболочкой для ядра по умолчанию (AppKernel). Кэширующее ядро и есть тот самый обратный прокси.
Для того чтобы активировать кэширование, модифицируйте код фронт-контроллера таким образом, чтобы он использовал кэширующее ядро:
<?php
// 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(), который возвращает строковое представление того, что происходит на кэширующем уровне. В dev окружении вы можете использовать его для отладки и проверки вашей стратегии кэширования:
error_log($kernel->getLog());
Объект AppCache имеет конфигурацию по умолчанию, но вы можете конфигурировать и настраивать его опции посредством переопределения метода getOptions():
<?php
// 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,
);
}
}
Совет
Для изменения опции debug переопределять getOptions() не обязательно, так как она автоматически принимает значение параметра debug от AppKernel.
Ниже представлен список основных опций:
Если debug имеет значение true, Symfony2 автоматически добавляет в ответ заголовок X-Symfony-Cache, содержащий полезную информацию о числе срабатываний кэша и о числе не найденных ответов в кэше.
Примечание
Быстродействие обратного прокси Symfony2 не зависит от сложности приложения. Это достигается за счёт того, что ядро приложения загружается лишь в том случае, когда к нему требуется перенаправить входящий запрос.
Для того, чтобы получить пользу от кэширования, ваше приложение должно иметь возможность сообщить, какие ответы могут быть кэшированы, а также правила, которые будут указывать когда и как истекает срок действия этого кэша. Этого можно достичь при помощи HTTP заголовков для кэширования ответов.
Совет
Имейте в виду, что “HTTP” это не более чем язык (простой текстовый язык), который веб клиенты (например, браузеры) и веб серверы используют для коммуникаций между собой. Когда мы говорим об HTTP кэшировании, мы говорим о части этого языка, которая позволяется клиентам и серверам обмениваться информацией, относящейся к кэшированию.
Спецификация HTTP содержит четыре заголовка, относящихся к кэшированию:
Наиболее важным и многосторонним является заголовок Cache-Control, который на самом деле является коллекцией разнообразной информации о кэшировании.
Примечание
Каждый из заголовков будет детально рассмотрен в секции Модели кэширования в HTTP: expiration и validation.
Заголовок Cache-Control уникален за счёт того, что он содержит не одно конкретное значение, а много различных данных о кэшируемости ответа. Каждая новая порция данных отделяется запятой:
Cache-Control: private, max-age=0, must-revalidate
Cache-Control: max-age=3600, must-revalidate
Symfony предоставляет методы для более удобного управления заголовком Cache-Control:
<?php
//...
$response = new Response();
// пометить ответ как public или private
$response->setPublic();
$response->setPrivate();
// установить max age для private и shared ответов
$response->setMaxAge(600);
$response->setSharedMaxAge(600);
// установить специальную директиву Cache-Control
$response->headers->addCacheControlDirective('must-revalidate', true);
Кэширующие шлюзы и прокси рассматривают “общие” кэши как кэшированный контент, который используется более чем одним пользователем. Если будет случайно сохранён ответ, специфичный для отдельного пользователя, впоследствии он может быть отправлен множеству различных пользователей. Представьте, что информация о вашем счёте была кэширована и будет отправлена любому пользователю, который запросит свою собственную страницу со счётом!
Для того чтобы корректно обработать эту ситуацию, каждый ответ может быть объявлен публичным или же частным:
Symfony действует консервативно и помечает каждый ответ как частный. Для того чтобы получить преимущества от использования публичных кэшеров (в том числе и обратного прокси Symfony2), ответ должен быть помечен как публичный (public).
HTTP кэширование работает лишь для “безопасных” HTTP методов (таких как GET и HEAD). Под безопасностью этих методов понимается, что вы никогда не измените состояние приложения при обработке таких запросов (при этом вы, конечно, можете логгировать информацию, кэшировать данные и т.д.). Это ограничение имеет два следствия:
HTTP 1.1 по умолчанию разрешает кэширование, если явно не указан заголовок Cache-Control. На практике, большинство кэшеров ничего не делают, если запросы имеют куки, авторизационный заголовок, используют небезопасные методы (т.е. PUT, POST, DELETE), или когда ответ имеет перенаправляющий статус-код (например, 301 или 302).
Symfony2 автоматически устанавливает разумно-консервативный заголовок Cache-Control, если разработчик не задал правила кэширования явно. Эти умолчания следуют следующим правилам:
Спецификация HTTP определяет две модели кэширования:
Целью обоих этих моделей является следующая: не генерировать один и тот же ответ дважды, если в кэше уже есть “свежий” ответ, сохранённый там ранее.
Модель окончания срока действия более эффективная и простая из двух поддерживаемых моделей кэширования и должна использоваться везде, где это возможно. Когда ответ кэшируется со сроком окончания действия, кэш будет хранить ответ и возвращать его на клиент напрямую, не затрагивая приложение, пока срок действия не окончится.
Модель окончания срока действия может быть задействована с использованием двух похожих HTTP заголовков: Expires или Cache-Control.
Следуя спецификации HTTP, “заголовок Expires содержит дату/время, после которого этот ответ будет считаться просроченным”. Заголовок Expires может быть установлен при помощи метода setExpires() класса Response. Он принимает экземпляр DateTime в качестве аргумента:
<?php
//...
$date = new DateTime();
$date->modify('+600 seconds');
$response->setExpires($date);
Результирующий заголовок будет выглядеть следующим образом:
Expires: Thu, 01 Mar 2011 16:00:00 GMT
Примечание
Метод setExpires() автоматически конвертирует дату в зону GMT, как того требует спецификация.
Заголовок Expires имеет 2 ограничения. Первое, часы на веб-сервере и и часы кэшера (например, браузера) должны быть синхронизированными. Второе, следует из спецификации и гласит, что “HTTP/1.1 серверы никогда не должны устанавливать дату Expires более чем на один год вперёд”.
Поскольку заголовок Expires имеет ограничения, вы должны использовать заголовок Cache-Control. Вспоминайте, что заголовок Cache-Control используется для указания различных директив, относящихся к кэшированию. Для окончания срока действия имеются две директивы, max-age и s-maxage. Первая используется всеми кэшерами, в то время как вторая используется лишь “общими” (shared) кэшами:
<?php
//...
// Устанавливаем число секунд, после которого ответ более не будет считаться свежим
$response->setMaxAge(600);
// Тоже что и выше, но только для общих кэшей
$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 - это строковый заголовок (называемый “entity-tag”), который единственным образом идентифицирует представление целевого ресурса. Он генерируется и устанавливается всецело внутри вашего приложения, так что вы можете понять, к примеру, соответствует ли кэшированный ресурс /about тому, который ваше приложение собирается вернуть. Заголовок ETag похож на отпечатки пальцев и используется для быстрого определения эквивалентны ли две версии ресурса. Как и отпечатки пальцев, каждый ETag должен быть уникальным для любого представления одного и того же ресурса.
Давайте взглянем на простую реализацию, которая генерирует ETag в виде md5 хэша от контента:
<?php
//...
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). Если они совпадают, этот метод автоматически устанавливает для Response статус код 304.
Этот алгоритм достаточно простой и вполне типичный, но вам нужно создать экземпляр Response целиком, перед тем как вы получите возможность сравнить ETag’и, а это весьма расточительно. Другими словами, этот подход сохраняет пропускную способность, но не ресурсы CPU.
В секции Оптимизация вашего кода при помощи метода валидации мы покажем как можно использовать валидацию более интеллигентно и определять валидность кэша без излишних затрат ресурсов сервера.
Совет
Symfony2 также поддерживает “слабые” ETag’и - для этого надо передать true в качестве второго аргумента в метод :method:`Symfony\\Component\\HttpFoundation\\Response::setETag`.
Заголовок Last-Modified - это второй возможный способ валидации. Следуя спецификации HTTP, “Заголовок Last-Modified содержит дату и время, когда представление ресурса было изменено в последний раз, по версии исходного сервера”. Другими словами, приложение принимает решение о том, должен ли быть обновлён кэшированный контент, основываясь на том, изменялся ли он со времени кэширования.
Например, вы можете использовать дату последнего обновления для всех объектов, необходимых для создания представления ресурса в качестве значения заголовка Last-Modified:
<?php
//...
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, установленном в ответе. Если они идентичны, Response будет установлен статус код 304.
Примечание
Заголовок запроса If-Modified-Since соответствует заголовку Last-Modified последнего ответа, отправленного клиенту для некоторого ресурса. Таким образом, клиент и сервер общаются друг с другом и определяют был ли ресурс обновлён с момента его кэширования.
Основная цель любой стратегии кэширования - понизить нагрузку на приложение. Иными словами, чем меньше делает ваше приложение для того, чтобы вернуть ответ 304, тем лучше. Метод Response::isNotModified() именно этим и занимается при использовании простого и эффективного шаблона:
<?php
//...
public function showAction($articleSlug)
{
// Получаем минимум информации для вычисления
// значений для заголовков ETag или Last-Modified
// (основываясь на запросе Request, данных, получаемых из базы данных
// или же из хранилища ключ-значение)
$article = // ...
// Создаём ответ Response с заголовком ETag и/или Last-Modified
$response = new Response();
$response->setETag($article->computeETag());
$response->setLastModified($article->getPublishedAt());
// Проверяем, что ответ не модифицировался для этого запроса
if ($response->isNotModified($this->getRequest())) {
// возвращаем ответ 304
return $response;
} else {
// делаем дополнительные действия, например, получаем дополнительные данные
$comments = // ...
// или отображаем шаблон при помощи $response, который был создан ранее
return $this->render(
'MyBundle:MyController:article.html.twig',
array('article' => $article, 'comments' => $comments),
$response
);
}
}
Если ответ Response не модифицировался, метод isNotModified() автоматически устанавливает статус код ответа в 304, удаляет контент и удаляет некоторые заголовки, которые не должны присутствовать в ответе 304 (см. метод :method:`Symfony\\Component\\HttpFoundation\\Response::setNotModified`).
Ранее вы узнали, что каждый URI имеет единственное представление целевого ресурса. По умолчанию, HTTP кэширование выполняется с использованием URI ресурса в качестве ключа к значению кэша. Если два человека запросят один и тот же URI для кэшируемого ресурса, второй клиент получит уже кэшированную версию.
Иногда этого не достаточно и требуется кэшировать различные версии одного и того же URI, основываясь на значении одного или нескольких заголовков. Например, если вы сжимаете страницы для клинентов, которые поддерживают сжатие, любой URI будет иметь два представления: одно для клиентов, поддерживающих сжатие, и одно для тех кто не поддерживает. Это определяется на основе значения заголовка запроса Accept-Encoding.
В этом случае, вам необходимо хранить обе версии ответа для некоторого ресурса - сжатую и не сжатую и возвращать ее, основываясь на значении заголовка запроса Accept-Encoding. Этого можно достичь при помощи заголовка ответа Vary, который является списком (разделители - запятые) различных заголовков, чьи значения переключают различные представления запрошенного ресурса:
Vary: Accept-Encoding, User-Agent
Совет
Заголовок Vary из примера выше позволяет кэшировать различные версии для каждого ресурса, основываясь на URI и значении заголовков запроса Accept-Encoding и User-Agent.
Объект Response предоставляет простой интерфейс для управления заголовком Vary:
<?php
//...
// устанавливаем один заголовок vary
$response->setVary('Accept-Encoding');
// устанавливаем несколько заголовков vary
$response->setVary(array('Accept-Encoding', 'User-Agent'));
Метод setVary() принимает в качестве параметра имя заголовка или же массив наименований заголовков, на основании значений которых необходимо варьировать ответ.
Вы можете использовать окончание срока действия совместно с валидацией в одном и том же экземпляре Response. Если окончание срока действия работает раньше валидации, вы сможете получить лучшие преимущества от обеих моделей. Другими словами, используя совместно модели окончание срока действия и валидации вы можете проинструктировать кэш хранить контент пока с некоторым интервалом осуществляется (окончание срока действия) проверка, что контент всё ещё валиден.
Класс Response содержит также другие методы для работы с кэшем. Пример ниже иллюстрирует самые часто употребляемые из них:
<?php
// пометить ответ как "просроченный"
$response->expire();
// Форсировать возврат ответа 304 без контента
$response->setNotModified();
В дополнение к этому, все основные HTTP относящиеся к кэшу, могут быть установлены при помощи одного метода setCache():
<?php
// Установить заголовки для кэширования одним вызовом
$response->setCache(array(
'etag' => $etag,
'last_modified' => $date,
'max_age' => 10,
's_maxage' => 10,
'public' => true,
// 'private' => true,
));
Кэширующие шлюзы - это отличный способ сделать ваш сайт более производительным. Но они также имеют и одно ограничение: они могут кэшировать лишь страницы целиком. Если вы по каким-то причинам не можете кэшировать страницы целиком или в случае когда страница имеет несколько динамических частей, вы вышли из зоны удачи. К счастью, Symfony2 предоставляет решение для этих случаев, основанное на технологии ESI, или Edge Side Includes. Компания Akamaï создала эту спецификацию почти 10 лет назад, и она позволяет иметь для отдельных частей страницы различные стратегии кэширования.
Спецификация ESI описывает таги, которые вы можете добавить в ваши страницы для общения с кэширующим шлюзом. В Symfony2 реализован лишь один таг - include, так как это наиболее полезный таг вне контекста Akamaï:
<html>
<body>
Some content
<!-- Подключаем контент другой страницы -->
<esi:include src="http://..." />
More content
</body>
</html>
Примечание
Обратите внимание, в примере выше, что для ESI тага указан полный URL. ESI таг представляет собой фрагмент страницы, который можно получить по этому URL.
При обработке запроса, кэширующий шлюз получает страницу целиком из своего кэша или же запрашивает его у приложения. Если ответ содержит один или более ESI тагов, они обрабатываются тем же образом. Другими словами, кэширущий шлюз получает включённые фрагменты страниц из своего кэша, либо запрашивает эти фрагменты у приложения. Когда все ESI таги обработаны, шлюз включает все фрагменты в основную страницу и отправляет итоговый контент клиенту.
Всё это происходит незаметно на уровне кэширующего шлюза (т.е. вне вашего приложения). Как вы увидите далее, если вы захотите использовать преимущества, которые предоставляют ESI таги, Symfony2 позволит вам подключать их не прилагая особых усилий.
Во-первых, перед использованием ESI, убедитесь, что вы активировали их в настройках приложения:
# app/config/config.yml
framework:
# ...
esi: { enabled: true }
<!-- 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 вы можете кэшировать новости независимо от остальной страницы.
<?php
// ...
public function indexAction()
{
$response = $this->render('MyBundle:MyController:index.html.twig');
$response->setSharedMaxAge(600);
return $response;
}
В этом примере вы устанавливаете для всей страницы время жизни кэша в 10 минут. Затем, подключите новости в шаблон при помощи встраивания действия. Это можно сделать при помощи хелпера render (см. Внедрение контроллеров).
Так как встроенный контент поступает из другой страницы (или контроллера в данном случае), Symfony2 использует стандартный хэлпер render для конфигурирования ESI тага:
{% render '...:news' with {}, {'standalone': true} %}
<?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 “из коробки”.
Теперь для встроенного действия вы можете указать собственные правила кэширования, независимо от главной страницы:
<?php
public function newsAction()
{
// ...
$response->setSharedMaxAge(60);
}
При помощи ESI кэш страницы будет валидным в течение 600 секунд, но компонент новостей будет кэшироваться только на 60 секунд.
Требованием, при использовании ESI, является следующее: встроенное действие должно быть доступно через некоторый URL, чтобы кэширующий шлюз мог получить его контент независимо от остальной страницы. Конечно, действие не может быть доступным без маршрута, который указывает на него. Symfony2 заботится и об этом при помощи базового маршрута и контроллера. Чтобы ESI таг include работал, вы должны определить маршрут _internal:
# app/config/routing.yml
_internal:
resource: "@FrameworkBundle/Resources/config/routing/internal.xml"
prefix: /_internal
<!-- 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 (разрешив доступ по IP вашего обратного прокси). См. секцию Защита по IP главы Безопасность.
Самое большое преимущество этой стратегии кэширования заключается в том, что вы можете делать ваше приложение настолько динамическим, насколько это вам нужно, при этом обращаясь к приложению лишь тогда, когда это необходимо.
Примечание
При использовании ESI, помните, что вам всегда необходимо использовать директиву s-maxage вместо max-age. Это необходимо, так как браузер получает агрегированный ресурс, следовательно, он не заботится о вложенных компонентах и будет подчиняться директиве max-age и кэшировать страницу целиком, чего вы точно не захотите.
Хелпер render поддерживает две важных опции:
“В науке о компьютерах есть лишь две сложные вещи: аннулирование кэша и вопросы именования.” –Phil Karlton
Вы не должны заботиться об аннулировании кэша, так как аннулирование уже заложено в модели кэширования HTTP. Если вы используете модель валидации, вам не нужно ничего аннулировать по определению; если вы используете окончание срока действия и требуется аннулировать ресурс, это означает, что ранее вы для этого ресурса установили срок окончания далеко в будущее.
Примечание
Так как аннулирование кэша - это тема, специфичная для каждого конкретного обратного прокси, если вы специально не побеспокоились об этом - то с лёгкостью сможете переключаться между различными прокси ничего не меняя в коде вашего приложения.
На самом же деле, любой обратный прокси предоставляет способ для очистки кэша, но вы должны стараться избегать этого, насколько возможно. Наиболее типичный путь для очистки кэша для некоторого URL - запросить чего при помощи специального HTTP метода PURGE.
Ниже вы увидите как настроить обратный прокси Symfony2 для поддержки HTTP метода PURGE:
<?php
// 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->getStore()->purge($request->getUri())) {
$response->setStatusCode(404, 'Not purged');
} else {
$response->setStatusCode(200, 'Purged');
}
return $response;
}
}
Осторожно
Вы должны защитить метод PURGE каким-либо образом, чтобы не допускать возможности очистки кэша случайными людьми.
Symfony2 создан таким образом, чтобы следовать проверенным правилам “движения” по дорогам HTTP. Кэширование - не исключение. Настройка системы кэширования Symfony2 подразумевает близкое знакомство с моделью кэширования HTTP и её эффективное использование. Это означает, что вместо того, чтобы полагаться только на документацию Symfony2 и примеры кода, вы получаете доступ к целому миру знаний, относящихся к кэшированию в HTTP и кэширующим шлюзам, таким как Varnish.