如何在产品页面中包含动态块并启用整页缓存?

时间:2012-02-02 21:39:09

标签: caching magento

我们想在产品页面添加动态块。问题是产品页面具有完整页面缓存(由于速度问题,我们无法将其关闭)。我们希望根据登录用户的帐户在每个产品页面上显示不同的信息,并且因产品而异。

我创建了一个单独的块,它有自己的缓存,但是它显示的是上一个产品页面中的相同块。我正在尝试更改它的缓存方法,因此它不会保存以前产品页面中的缓存。

我最初几次访问产品页面,然后突然开始显示一个Magento错误页面,上面写着“网站在检索http://www.mycompany.com/productpage.html时遇到错误。” 它可能已关闭以进行维护或配置不正确。“

这是我到目前为止所做的。

  

我创建 app/code/local/MyCompany/MyModule/PageCache/etc/config.xml 以添加 MyCompany_PageCache_Model。

     

然后我在 app/code/local/MyCompany/MyModule/PageCache/Model/Container/MyFile.php 中创建了控制缓存的文件    有了这些功能:

protected function _getCacheId()
{
    return 'CONSTANT_CACHE' . md5($this->_placeholder->getAttribute('cache_id'));
}

protected function _saveCache($data, $id, $tags = array(), $lifetime = null)
{
    return false;
}

protected function _renderBlock()
{
    $blockClass = $this->_placeholder->getAttribute('block');
    $template = $this->_placeholder->getAttribute('template');

    $block = new $blockClass;
    $block->setTemplate($template);
    $block->setLayout(Mage::app()->getLayout());
    return $block->toHtml();
}

我还使用 CONSTANT_CACHE

的占位符在目录/ etc 下创建了 cache.xml

上面的语法是否不正确,或者有更简单的方法吗?

4 个答案:

答案 0 :(得分:122)

概述

为了回答我需要先解释一下。 Magento FPC流程知道四种状态。

  1. 缓存中的页面,没有动态块
  2. 缓存中的页面,缓存的动态块
  3. 缓存中的页面,未缓存的动态块
  4. 页面不在缓存中
  5. 在没有初始化完整Magento应用程序的情况下处理状态1和2。状态3和4要求初始化应用程序并处理路由。出于这个原因,如果可能的话,目的是服务于州1和2的请求,否则你将失去FPC可能改进的很大一部分。

    州1

    状态1从开发人员的角度来看很无聊,无所事事,所以让我们继续......

    州2

    在状态2中,页面包含动态块。 目前,Magento尚未完全初始化 FPC处理器加载缓存页面并在其中找到动态块的占位符 通过分析占位符,处理器能够识别动态块的容器类,实例化它,并在其上调用applyWithoutApp($content)。 (该方法的名称是指到目前为止Magento应用程序尚未初始化的事实)。 然后容器尝试使用方法$this->_getCacheId()返回的缓存键从块缓存加载动态块内容。
    如果返回缓存键并且可以加载缓存条目,则容器类将$content中的占位符替换为缓存块输出并完成FPC。
    到目前为止,没有产生太多的开销。

    州3

    因此状态2中的applyWithoutApp($content)无法获取并传递动态块内容,因此即使在FPC中找到了页面的其余部分,也需要生成块内容。
    为此,FPC模块将请求设置为pagecache/request/process,并遵循常规Magento应用程序初始化和路由。
    这意味着在状态2下产生了更多的开销,即使它仍然比没有FPC的常规页面加载好一些,因为例如跳过URL重写。
    最后,前端控制器和标准路由器将请求委托给RequestController::processAction()方法 该方法为动态块提取先前实例化的容器类,并在其上调用applyInApp($content) 此方法运行$this->_renderBlock()以实例化实际块类并返回其输出。您已根据自己的问题实施了此方法。 FPC现在可以用块内容替换占位符并传送页面 需要注意的一件事是,这不是常规的产品详情页面请求,例如Mage::registry('current_product')不可用! 根据您的块实现,这可能会影响块级缓存或动态块的内容生成。我怀疑这可能是你的问题源于何处,但我会进一步解决可能的解决方法。

    州4

    在这种状态下,FPC没有找到所请求页面的缓存记录,因此Magento像往常一样生成页面,例如产品详细信息页面输出由Mage_Catalog_ProductController::viewAction()创建 根据{{​​1}},配置为动态的所有块都包含在占位符标记中 占位符标记包含参数,稍后将其传递给第2步和第3步的容器对象。始终设置的唯一参数是容器和块类名。但几乎总是设置cache.xmlcache_id 在容器类中,可以使用template访问这些值(就像在容器的_getCacheId()方法中所做的那样)。

    即使你在这个冗长的答案中找到了大部分内容,这也是你可能感兴趣的地方。如果您需要其他值来生成块缓存ID或块输出(例如,产品ID或客户ID),可以将这些值设置为占位符的参数

    为此,您需要在块$this->_placeholder->getAttribute('cache_id')方法返回的数组上设置它们,并使用字符串作为数组键。如果使用数字数组索引,则不会将其设置为占位符的参数

    getCacheKeyInfo()

    现在可以使用public function getCacheKeyInfo() { $info = parent::getCacheKeyInfo(); $info['current_product_id'] = Mage::registry('current_product')->getId(); $info['customer_id'] = Mage::getSingleton('customer/session')->getCustomerId(); return $info; } 在容器类中访问这些值。

    结论

    您可能不想覆盖容器类中的$this->_placeholder->getAttribute('current_product_id')以返回_saveCache()。而是将客户ID和产品ID包含在false返回的字符串中。这样每个客户都可以获得自己的缓存条目。由于_getCacheId()可以从缓存中保存和加载动态块(如果同一客户查看两次页面),则会减少一些开销。

    applyWithoutApp()中设置您需要的其他值,以便块能够在其上生成内容,例如

    _renderBlock()

    在块的方面,包括缓存信息数组中的产品ID和客户ID,将确保每个客户获得所请求页面的正确输出,即使缓存块也是如此。

    我无法确定,(您还没有提供阻止代码),但我怀疑您使用的缓存ID并不包含唯一映射所需的所有参数将块缓存记录到正确的产品。

    使用这些步骤并了解如何将参数传递给动态块容器,即使在创建自定义动态块时,也可以保留大部分FPC性能增益。我希望这些信息足以让您能够找到您正在描述的问题并进行修复。

答案 1 :(得分:5)

通常,如果您想要个性化要存储在整页缓存中的页面,可以使用两种方法。

  1. 如果您的反向代理支持它,您可以使用ESI(Edge Side Includes)并适当地标记您的模板。 ESI允许您在生成的HTML中插入标记,个性化内容应该放在该标记中,然后您的代理将在需要时从您的应用服务器的相应控制器路径中仅请求个性化内容。如果您使用的是VarnishESI is available。 Magento的Lightspeed扩展程序有一个名为“Hole Punching"的功能,它可以执行类似的操作。
  2. 如果您无法使用ESI或打孔,则另一个选项是允许将主页缓存在整页缓存中,并使用一些javascript来发出单独的Ajax请求并获取信息你需要。

答案 2 :(得分:1)

我非常感谢Vinai的回答。 另外,我想建议Gordon Lesti支持打孔的FPC扩展。 You can get it over here

有关如何使用此扩展程序进行打孔的说明,建议您访问this page

我希望它可以帮助那些不太熟悉这些概念的人。

答案 3 :(得分:1)

没有必要:

  $info['current_product_id'] = Mage::registry('current_product')->getId();

您可以使用此方法:

$this->_getProductId()

在Enterprise_PageCache_Model_Container_Abstract

中实现