magento快取系列詳解:如何快取一個block
阿新 • • 發佈:2018-11-07
magento是基於zend framework的,所以cache的使用基本也繼承了他的一些特性。我們要知道快取中有幾個重要的概念:
資料本身;資料的標識;快取生命期;快取操作介面;
Zend_Cache 的使用比較簡單, 它可以把資料儲存到 File, Memcache, SQLite 等介質(稱為後端, Backend)中. 還有前端(Frontend), 主要用來對要快取的資料進行轉換, 如序列化.
Zend Framework 後端主要有
apc (Zend_Cache_Backend_Apc),
files (Zend_Cache_Backend_File),
memcached (Zend_Cache_Backend_Memcached)
and some more..
database (Varien_Cache_Backend_Database)
eaccelarator (Varien_Cache_Backend_Eaccelarator).
資料本身;資料的標識;快取生命期;快取操作介面;
Zend_Cache 的使用比較簡單, 它可以把資料儲存到 File, Memcache, SQLite 等介質(稱為後端, Backend)中. 還有前端(Frontend), 主要用來對要快取的資料進行轉換, 如序列化.
Zend Framework 後端主要有
apc (Zend_Cache_Backend_Apc),
files (Zend_Cache_Backend_File),
memcached (Zend_Cache_Backend_Memcached)
and some more..
Magento 增加了幾種:
eaccelarator (Varien_Cache_Backend_Eaccelarator).
為了更深入的理解magento cache,本文就以快取某個block為例,分析下他的工作流程和原理。在magento中,所有的block都是繼承自Mage_Core_Block_Abstract,當輸出某個block到頁面時,會呼叫到這個類的toHtml方法,該方法中有如下code片段:
繼續看本類中的function _saveCache($data)$html = $this->_loadCache();//載入快取,假設為第一次載入,還沒有被快取過,進入if程式碼段 if ($html === false) { $translate = Mage::getSingleton('core/translate'); /** @var $translate Mage_Core_Model_Translate */ if ($this->hasData('translate_inline')) { $translate->setTranslateInline($this->getData('translate_inline')); } $this->_beforeToHtml(); $html = $this->_toHtml(); $this->_saveCache($html);//這句程式碼很簡單,但卻在內部完成了大量的工作,下面進入它內部深入分析 if ($this->hasData('translate_inline')) { $translate->setTranslateInline(true); } }
開啟Mage_Core_Model_App 的function saveCachefunction _saveCache($data) /** * Save block content to cache storage * * @param string $data * @return Mage_Core_Block_Abstract */ protected function _saveCache($data) { if (is_null($this->getCacheLifetime()) || !Mage::app()->useCache(self::CACHE_GROUP)) { return false; } $cacheKey = $this->getCacheKey(); /** @var $session Mage_Core_Model_Session */ $session = Mage::getSingleton('core/session'); $data = str_replace( $session->getSessionIdQueryParam() . '=' . $session->getEncryptedSessionId(), $this->_getSidPlaceholder($cacheKey), $data ); //可以肯定進入了Mage_Core_Model_App 的function saveCache Mage::app()->saveCache($data, $cacheKey, $this->getCacheTags(), $this->getCacheLifetime()); return $this; }
/**
* Saving cache data
*
* @param mixed $data
* @param string $id
* @param array $tags
* @return Mage_Core_Model_App
*/
public function saveCache($data, $id, $tags=array(), $lifeTime=false)
{
$this->_cache->save($data, $id, $tags, $lifeTime);
return $this;
}
可以看到$this->_cache這個物件呼叫了save,繼續看$this->_cache到底屬於哪個類的例項,在本類中找到protected function _initCache(),code如下:
/**
* Initialize application cache instance
*
* @return Mage_Core_Model_App
*/
protected function _initCache()
{
$this->_isCacheLocked = true;
$options = $this->_config->getNode('global/cache');
if ($options) {
$options = $options->asArray();
} else {
$options = array();
}
//例項化Mage_Core_Model_Cache,並傳入引數$options
$this->_cache = Mage::getModel('core/cache', $options);
$this->_isCacheLocked = false;
return $this;
}
上面Mage::getModel('core/cache', $options)的$options是app/etc/local.xml中節點global/cache下的配置,假設ocal.xml如下:
<config>
<global>
<cache>
<backend>apc</backend>
<slow_backend>File</slow_backend>
<auto_refresh_fast_cache>1</auto_refresh_fast_cache>
<prefix>MYSHOP_</prefix>
<default_priority>10</default_priority>
</cache>
</global>
</config>
能看到$this->_cache是例項化了例項化Mage_Core_Model_Cache,所以Mage_Core_Model_App 中的$this->_cache->save($data, $id, $tags, $lifeTime)即呼叫的Mage_Core_Model_Cache的function save。進入Mage_Core_Model_Cache的save:
/**
* Save data
*
* @param string $data
* @param string $id
* @param array $tags
* @param int $lifeTime
* @return bool
*/
public function save($data, $id, $tags=array(), $lifeTime=null)
{
/**
* Add global magento cache tag to all cached data exclude config cache
*/
if (!in_array(Mage_Core_Model_Config::CACHE_TAG, $tags)) {
$tags[] = Mage_Core_Model_App::CACHE_TAG;
}
return $this->_frontend->save((string)$data, $this->_id($id), $this->_tags($tags), $lifeTime);
}
可以看到$this->_frontend->save(...)這句,那麼這裡的_frontend是什麼呢?繼續看此類的建構函式(只列出部分code):
$backend = $this->_getBackendOptions($options);
$frontend = $this->_getFrontendOptions($options);
$this->_frontend =Zend_Cache::factory('Varien_Cache_Core', $backend['type'], $frontend, $backend['options'],true, true, true);
沒錯_frontend就是類Varien_Cache_Core的一個例項,因為Varien_Cache_Core是繼承自Zend_Cache_Core,所以這裡_frontend實際上例項化的是Zend_Cache_Core,即呼叫的是Zend_Cache_Core的save,再繼續分析,開啟Zend_Cache_Core,找到function save它裡面包含了如下程式碼片段:
if (($this->_extendedBackend) && ($this->_backendCapabilities['priority'])) {
$result = $this->_backend->save($data, $id, $tags, $specificLifetime, $priority);
} else {
$result = $this->_backend->save($data, $id, $tags, $specificLifetime);
}
可以看到這裡實際上又變成了_backend的來呼叫的save,(開頭我們已經說了backend是用來儲存資料的),那麼這裡_backend到底是哪個backend(file,db,apc...)呢?繼續分析,在這個類裡面有一個方法setBackend:
/**
* Set the backend
*
* @param Zend_Cache_Backend $backendObject
* @throws Zend_Cache_Exception
* @return void
*/
public function setBackend(Zend_Cache_Backend $backendObject)
{
$this->_backend= $backendObject;
// some options (listed in $_directivesList) have to be given
// to the backend too (even if they are not "backend specific")
$directives = array();
foreach (Zend_Cache_Core::$_directivesList as $directive) {
$directives[$directive] = $this->_options[$directive];
}
$this->_backend->setDirectives($directives);
if (in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_backend))) {
$this->_extendedBackend = true;
$this->_backendCapabilities = $this->_backend->getCapabilities();
}
}
裡面有$this->_backend= $backendObject這句,$backendObject是傳入的引數,那麼是何時呼叫的setBackend呢?實際上,這個方法是在上邊Mage_Core_Model_Cache裡的save方法中呼叫$this->_frontend = Zend_Cache::factory('Varien_Cache_Core', $backend['type'], $frontend, $backend['options'], true, true, true);這句的的時候,已經被呼叫了,可以開啟Zend_Cache的factory 方法檢視,會發現有一句$frontendObject->setBackend($backendObject);引數$backendObject是factory傳入的引數$backend['type'],$backend['type']到底是什麼呢,我們在Mage_Core_Model_Cache的建構函式中看到$backend=$this->_getBackendOptions($options)這句,所以$backend是在本類的_getBackendOptions中被設定的,找到_getBackendOptions,code如下:
/**
* Get cache backend options. Result array contain backend type ('type' key) and backend options ('options')
*
* @param array $cacheOptions
* @return array
*/
protected function _getBackendOptions(array $cacheOptions)
{
$enable2levels = false;
$type = isset($cacheOptions['backend']) ? $cacheOptions['backend'] : $this->_defaultBackend;
if (isset($cacheOptions['backend_options']) && is_array($cacheOptions['backend_options'])) {
$options = $cacheOptions['backend_options'];
} else {
$options = array();
}
$backendType = false;
switch (strtolower($type)) {
case 'sqlite':
if (extension_loaded('sqlite') && isset($options['cache_db_complete_path'])) {
$backendType = 'Sqlite';
}
break;
case 'memcached':
if (extension_loaded('memcache')) {
if (isset($cacheOptions['memcached'])) {
$options = $cacheOptions['memcached'];
}
$enable2levels = true;
$backendType = 'Memcached';
}
break;
case 'apc':
if (extension_loaded('apc') && ini_get('apc.enabled')) {
$enable2levels = true;
$backendType = 'Apc';
}
break;
case 'xcache':
if (extension_loaded('xcache')) {
$enable2levels = true;
$backendType = 'Xcache';
}
break;
case 'eaccelerator':
case 'varien_cache_backend_eaccelerator':
if (extension_loaded('eaccelerator') && ini_get('eaccelerator.enable')) {
$enable2levels = true;
$backendType = 'Varien_Cache_Backend_Eaccelerator';
}
break;
case 'database':
$backendType = 'Varien_Cache_Backend_Database';
$options = $this->getDbAdapterOptions();
break;
default:
if ($type != $this->_defaultBackend) {
try {
if (class_exists($type, true)) {
$implements = class_implements($type, true);
if (in_array('Zend_Cache_Backend_Interface', $implements)) {
$backendType = $type;
}
}
} catch (Exception $e) {
}
}
}
if (!$backendType) {
$backendType = $this->_defaultBackend;
foreach ($this->_defaultBackendOptions as $option => $value) {
if (!array_key_exists($option, $options)) {
$options[$option] = $value;
}
}
}
$backendOptions = array('type' => $backendType, 'options' => $options);
if ($enable2levels) {
$backendOptions = $this->_getTwoLevelsBackendOptions($backendOptions, $cacheOptions);
}
return $backendOptions;
}
程式碼有些長,但這個方法很重要,後端具體使用什麼型別儲存快取都是在這裡被設定和轉換的,有程式碼分析可知,預設的Backend為file,其他有sqlite, apc, memcached, xcache, eaccelerator, database等幾種型別,當backend是memcached, apc,xcache時,magento就自動開啟two level cache,具體設定可以自行看function _getTwoLevelsBackendOptions。
接著上面的save分析,本例因為backend設定為apc,所以backend最終的type為TwoLevels(apc預設開啟TwoLevels),即呼叫的是Zend_Cache_Backend_TwoLevels的save 方法;save成功之後,第二次需要輸出這個block時,首先會$this->_loadCache(),對於已經快取過的block,會直接從快取中取出。
以上就是magento快取某個block的整個流程,至於具體如何去save,因backend不同會有所不同,可以自行參看原始碼;clean cache原理類似,不再贅述。
本例以magento 1.5版本為基礎進行分析,不同版本code可能會有所不同;
本文連結:http://blog.csdn.net/shangxiaoxue/article/details/7710549