Patrones : Cache Manager (PHP5)
Cuando :
Es un elemento crucial para cualquier aplicación que obtenga informacion de un centro de computo que tome tiempo en procesar una solicitud , lo anterior debido a que que se deben inicializar estructuras para mantener las copias de datos/buffers/ , generalmente inicializar un descriptor , sobre eso realizar la lógica del protocolo , solicitar el recurso, inicializar la transferencia y finalmente copiar en las estructuras previamente allocadas.
La solución mas simple es realizar una sola vez este proceso durante un periodo T , manteniendo esta información en algun backend accesible por el proceso sin necesidad de realizar nuevamente el costoso proceso descrito anteriormente de forma transparente para la aplicación.
Como :
Si los datos solicitados se encuentran en el cache ( hit ) , la solicitud será atendida leyendolo desde el caché, lo que acelerará el tiempo de respuesta. En el caso de que el dato solicitado no se encuentre, será solicitado a la fuente de datos en su ubicación original ( independiente del protocolo ) y almacenada en el caché intermedio. Del mismo modo, la siguiente solicitud para el mismo recurso será atendida desde el caché previamente almacenado.
Código:
La implementación utiliza adicionalmente un patrón Factory para obtener una instancia dependiendo del tipo de backend seleccionado , tambien utiliza un patrón singleton en vista de asegurar la existencia de una sola instancia en un ciclo de ejecucción.
<?php
/*
* Partial implementation of a Cache Manager Pattern for general use.
*
* @author Jorge Niedbalski R.
* @(#)Data::Cache::CacheManager
*
*/
class CacheObjectException extends Exception {}
class CacheObject {
private $is_url;
private $keyname;
private $lifetime;
private $value;
private $backend;
public function __construct($backend = NULL, $keyname, $value, $lifetime, $is_url = FALSE)
{
$this->backend = $backend;
$this->keyname = $keyname;
$this->value = $value;
$this->lifetime = $lifetime;
$this->is_url = TRUE;
}
public function __destruct() {}
public function getKeyName()
{
return ( $this->keyname );
}
public function getLifeTime()
{
return ( $this->lifetime );
}
public function getValue()
{
if($this->is_url) {
$this->value = file_get_contents($this->keyname);
if($http_response_header[0] != 'HTTP/1.1 200 OK') {
throw new CacheObjectException(sprintf("Invalid response from resource %s", $this->keyname));
}
}
return ( $this->value );
}
public function getBackend()
{
return ( $this->backend );
}
public function isURL()
{
return ( $this->is_url );
}
public function setBackend($backend)
{
$this->backend = $backend;
}
public function setKeyName($name, $url = FALSE)
{
$this->keyname = $name;
}
public function setIsURL($is_url = FALSE)
{
$this->is_url = $is_url;
}
public function setLifeTime($lifetime)
{
$this->lifetime = $lifetime;
}
public function setValue($value)
{
$this->value = $value;
}
public function fetch()
{
try {
return ( CacheManager::fetchObject($this) );
} catch(CacheManagerException $e) {
throw new CacheObjectException($e->getMessage());
}
}
}
class CacheBackendFactoryException extends Exception {}
class CacheBackendFactory
{
protected static $_instance; //single instance for each backend on the same execution time
public static function getInstance($backend)
{
if(self::$_instance == NULL) {
$backend_class = sprintf("%sBackend", $backend);
$backend_file = sprintf("%s/Backends/%s.php", dirname(__FILE__), $backend_class);
if(!file_exists($backend_file)) {
throw new CacheBackendFactoryException(sprintf("File %s not found on available backend classes\n", $backend_file));
}
require_once($backend_file);
self::$_instance = new $backend_class();
}
return ( self::$_instance );
}
}
class CacheException extends Exception {}
class Cache {
public static function fetchObject(CacheObject $object)
{
try {
return ( CacheBackendFactory::getInstance($object->getBackend())->fetchObject($object) );
} catch(CacheBackendFactoryException $e) {
throw new CacheException($e->getMessage());
} catch(CacheBackendException $e) {
throw new CacheException($e->getMessage());
}
}
public static function addObject(CacheObject $object)
{
try {
return ( CacheBackendFactory::getInstance($object->getBackend())->saveObject($object) );
} catch(CacheBackendFactoryException $e) {
throw new CacheException($e->getMessage());
} catch(CacheBackendException $e) {
throw new CacheException($e->getMessage());
}
}
}
class CacheManagerException extends Exception {}
class CacheManager {
public static function fetchObject(CacheObject $object)
{
try {
return ( Cache::fetchObject($object) );
} catch(CacheException $e) {
throw new CacheManagerException($e->getMessage());
} catch(ObjectKeyNotFoundException $e) {
try {
return ( Cache::addObject($object) );
} catch(ObjectCreaterException $e) {
throw new CacheManagerException($e->getMessage());
} catch(CacheException $e) {
throw new CacheManagerExeption($e->getMessage());
}
}
}
}
interface Interface_CacheBackend {
public function fetchObject(CacheObject $object);
public function saveObject(CacheObject $object);
}
?>
Una implementación de Backend , ha sido realizada utilizando Memcached como centro de almacenamiento en un esquema llave-valor (debes tener instalada la extensión Pecl Memcache ) :
<?php
define(MEMCACHE_HOST, '127.0.0.1');
define(MEMCACHE_PORT, 11211);
class ObjectKeyNotFoundException extends Exception {}
class CacheBackendException extends Exception {}
class MemcacheBackend implements Interface_CacheBackend {
private $conn;
public function __construct()
{
$this->conn = new Memcache();
$this->conn->connect(MEMCACHE_HOST, MEMCACHE_PORT);
}
public function fetchObject(CacheObject $object)
{
if( ($data = $this->conn->get($object->getKeyName()) ) == FALSE ) {
throw new ObjectKeyNotFoundException();
} else {
return ( $data );
}
}
public function saveObject(CacheObject $object)
{
if ( $this->conn->set($object->getKeyName(), $object->getValue(), 0, $object->getLifeTime()) ) {
return ( $object->getValue() );
}
throw new CacheBackendException("Cannot save cache key %s", $object->getKeyName());
}
}
?>
Otros
Es un patrón de uso cotidiano para cualquier programador que se interese en el rendimiento de su aplicación. Es importante que sea usado con un backend que asegure un buen tiempo de respuesta.
Referencias
