С Symfony2 почему теги ESI внутри кэшированных ответов игнорируются?
У меня есть приложение для электронной коммерции, которое я пытаюсь настроить для кэширования - сначала с помощью обратного прокси-сервера Symfony2, но затем, в конечном итоге, через Varnish на производстве. Я использую Symfony 2.1.8 на Apache2.
Моя проблема в том, что я не могу заставить тэги ESI перепроверяться для каждого запроса, когда кэшируется основное действие контроллера (важно для частного контента, такого как содержимое корзины), но я не понимаю, почему.
Например, я кеширую домашнюю страницу следующим кодом:
public function indexAction(Request $request)
{
// check cache
$homepage = $this->getHomepage();
$response = new Response();
$response->setPublic();
$etag = md5('homepage'.$homepage->getUpdated()->getTimestamp());
$response->setETag($etag);
$response->setLastModified($homepage->getUpdated());
if ($response->isNotModified($request))
{
// use cached version
return $response;
}
else
{
return $this->render(
'StoreBundle:Store:index.html.twig',
array(
'page' => $homepage
),
$response
);
}
}
Представленный шаблон расширяет базовый шаблон макета, который включает в себя следующий ESI для отображения корзины:
{% render 'PurchaseBundle:Basket:summary' with {}, { 'standalone': true } %}
(Изменить: после прочтения ответа Диего, я также использовал рекомендуемый синтаксис:
{% render url('basket_summary') with {}, {'standalone': true} %}
К сожалению, это не имело никакого значения.)
Я немного поигрался с кодом для описания корзины, но это то, что я имею в настоящее время.
public function summaryAction()
{
$response = new Response();
$response->setPrivate();
$response->setVary(array('Accept-Encoding', 'Cookie'));
if ($this->basket->getId())
{
$etag = md5($this->getUniqueEtag());
$response->setLastModified($this->basket->getUpdated());
}
else
{
$etag = md5('basket_summary_empty');
}
$response->setETag($etag);
if ($response->isNotModified($this->request))
{
// use cached version
return $response;
}
else
{
return $this->render(
'PurchaseBundle:Basket:summary.html.twig',
array(
'basket' => $this->basket
),
$response
);
}
}
На страницах, отличных от домашней страницы (которые еще не кэшированы), кэширование сводной корзины работает просто отлично, оно всегда отображает правильные данные. Только когда вы вернетесь на домашнюю страницу, вы увидите устаревшую информацию. Ведение журнала подтверждает, что summaryAction
не вызывается на главной странице, если indexAction
на самом деле оказывает.
редактировать
С помощью error_log($kernel->getLog())
после каждого запроса страницы я получаю это для некэшированной страницы:
GET /categories/collections: miss; GET /_internal/secure/PurchaseBundle:Basket:summary/none.html: stale, valid, store; GET /_internal/secure/CatalogBundle:Search:form/none.html: miss; GET /esi/menu/main: fresh
И это для кэшированной домашней страницы:
GET /: fresh
Я, должно быть, упускаю что-то очевидное, но документация не покрывает это, но подразумевает, что это именно то, для чего предполагается использовать ESI.
2 ответа
Похоже, что ESI в Symfony2 не работает с используемой вами структурой проверки кэша Latmodified/Etag. Смотрите здесь: https://groups.google.com/forum/?fromgroups=
Аналогичный вопрос здесь: Edge Side включает и проверочный кеш в Symfony 2
Я пытался сделать то же самое, что и вы, но заставить ESI работать только с помощью кэша истечения срока действия.
Начиная с 2.0.20/2.1.5 Symfony2 использует полный URL-адрес вместо логического пути контроллера, как указано в CVE-2012-6431. Итак, вместо вашего текущего ESI:
{% render 'PurchaseBundle:Basket:summary' with {}, { 'standalone': true } %}
Вы должны создать маршрут и затем использовать метод url на ветке:
{% render url('basket_summary') with {}, {'standalone': true} %}
Также обратите внимание, что сегодня (1 марта) была выпущена новая стабильная версия Symfony (2.2.0) с существенными улучшениями в управлении подзапросами. В этой новой версии вы можете использовать два подхода (извлеченные из основной версии главы HTTP Cache в книге):
{# you can use a controller reference #}
{{ render_esi(controller('...:news', { 'max': 5 })) }}
{# ... or a URL #}
{{ render_esi(url('latest_news', { 'max': 5 })) }}
Стоит также прочитать примечания к текущей версии связанной главы, так как они содержат полезную информацию и полезные советы, которые могут помочь вам найти актуальную проблему.