为什么在Symfony2中缓存响应时忽略ESI标签?

3
我有一个电子商务应用程序,我正在尝试通过Symfony2 Reverse Proxy进行缓存设置,但最终希望在生产环境中使用Varnish。我正在使用Apache2上的Symfony 2.1.8。
我的问题是,当主控制器动作被缓存时(对于像购物篮内容这样的私人内容非常重要),我无法重新检查每个请求的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 } %}

(编辑:在阅读Diego的答案后,我也使用了推荐的语法:

{% 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
        );
    }
}

在首页以外的页面上(尚未被缓存的页面),购物篮摘要缓存运作良好,它总是显示正确的数据。只有当您返回主页时才会看到过时的信息。日志确认了除非实际呈现 indexAction ,否则 summaryAction 不会在主页上调用。
编辑
在每个页面请求之后使用 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个回答

3

自从2.0.20/2.1.5版本开始,Symfony2采用完全合格的URL代替控制器逻辑路径,详见CVE-2012-6431。因此,您当前的ESI应该是:

{% render 'PurchaseBundle:Basket:summary' with {}, { 'standalone': true } %}

你需要创建一个路由,然后在twig上使用url方法:
{% render url('basket_summary') with {}, {'standalone': true} %}

此外请注意,今天(3月1日)发布了Symfony的新稳定版(2.2.0),其中改进了子请求管理。使用这个新版本,你可以采取两种方法(从The Book的HTTP缓存章节的主要版本中提取):
{# you can use a controller reference #}
{{ render_esi(controller('...:news', { 'max': 5 })) }}

{# ... or a URL #}
{{ render_esi(url('latest_news', { 'max': 5 })) }}

当前版本的链接章节中的侧注也值得阅读,因为它们包含了美味的信息和不错的提示,这些可能会帮助您找到实际的问题。

感谢提供的额外信息 - 我相信我在某个时候会升级到2.2版本。我已经看到了CVE注释,但是认为我也可以使用那种形式。我已经更改了逻辑路径以使用url('route'),清除了缓存,但仍然遇到同样的问题。 - Barry

2

谢谢回复,你在2.2上尝试过吗?我将很快尝试并报告结果,希望所有子请求的更改都能解决问题。 - Barry
我的测试是在2.0.23版本进行的,不确定2.2版本会怎样。祈求好运! - Jones03

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接