domingo, 18 de marzo de 2012

Como utilizar APC

¿Que es APC?

APC es un sistema de cache NO distribuido, que nos permite almacenar en memoria RAM los datos que necesitamos, además de un acelerador de PHP, por lo que también aumentará el rendimiento de nuestros scripts.

¿Como funciona APC?

APC una vez instalado, puede tener dos usos:
Acelerador de PHP
PHP es un lenguaje interpretado, por lo que no es el sistema operativo quien arranca nuestros scripts, si no que es PHP el que se encarga de interpretar lo que hemos escrito e interpretarlo generando su propio código máquina (opcode).
Ejecutando un script sin APC Cada vez que se ejecuta un script, sucede lo que se puede ver en la imagen superior. Si os fijáis, en un entorno de producción el paso de compilar el script no es necesario, ya que sabemos que ese archivo no va a cambiar, por lo que nos podríamos ahorrar la compilación del código y obtener siempre el mismo opcode compilado la primera vez. En este punto es donde entra APC. APC lo que hace por defecto es almacenar el opcode generado por el PHP y cuando PHP pide nuevamente el archivo, en vez de tenerlo que compilar de nuevo, APC se lo sirve directamente ahorrándonos todo el proceso de compilación, ahorrando así CPU y aumentando la velocidad de ejecución y respuesta de nuestros scripts.
Ejecutando un script con APC Por defecto APC cada vez que PHP le pide el opcode de un archivo el comprueba si el archivo ha sido modificado desde la última vez que obtuvo su opcode. De esta forma este sistema nos puede servir también en un entorno de desarrollo, ya que no tenemos que ir eliminando manualmente el opcode de la versión anterior de nuestro script. Esto nos trae un inconveniente, y es que como se puede ver en el gráfico, APC necesita realizar por cada petición un acceso a disco para comprobar la fecha de modificación del archivo y pese a que aún así es más rápido que compilar de nuevo nuestro script, es una operación completamente innecesaria en un entorno de producción donde sabes que los archivos no van a cambiar, y en caso de cambiar puedes hacer manualmente una limpieza de caché. Por eso APC nos provee del flag "stat", representado también en el flujo, que nos permite activar o desactivar ese comportamiento.

Sistema de caché
Desde PHP puedes decirle a APC que almacene un valor en memoria RAM, de tal forma que cuando queramos podemos volver a obtener ese dato de forma muy rápida. Esto puede servir por ejemplo para almacenar un resultado de una consulta que sea idéntica a todos los usuarios y que no queramos repetirla por cada conexión. De esta forma el primer usuario que ejecutaría la página haría la consulta a base de datos y almacenaría el resultado en APC. A partir de ese momento, todos los usuarios que accedieran al sitio no necesitarían obtener el resultado de base de datos, si no que lo obtendrían directamente de memoria RAM, por lo que la obtención del dato será muchísimo más rápida que obtenerla desde Mysql.

¿Como se usa?

Apc es una extensión de PHP, por lo que instalarla es tan sencillo como configurarla. Si tienes un sistema operativo linux basado en debian, es algo ran sencillo como:
$ sudo apt-get install php-apc
Para usarla, disponemos de una API que se puede consultar en la documentación oficial de PHP y que permite básicamente almacenar datos en memoria y obtenerlos. El funcionamiento es muy sencillo, ya que de forma básica solo necesitas utilizar las funciones apc_fetch para la recuperación de datos y apc_store para el almacenamiento de datos.
Os dejo un ejemplo para que veáis un uso muy básico, pero que cubre la gran mayoría de los casos
<?php
// Aquí solo se accede la primera vez
if ( !apc_exists( 'calculo_super_complicado' ) 
{
 $resultado = hacerCalculoMuyLentoYComplicado();
 apc_store( 'calculo_super_complicado', $resultado );
 echo $resultado;
}
//El resto de usuarios ejecutan esta parte
else 
{
 echo apc_fetch( 'calculo_super_complicado' );
}
?>

Al usar APC como sistema de caché, se tiene que tener en cuenta que por defecto APC tiene configurado un tamaño de caché de 32mb, por lo que cuando llegue a ese límite automáticamente las entradas menos usadas se irán descartando a medida que se añadan de nuevas. Si deseas borrar algo manualmente, solo tienes que usar la instrucción apc_delete y si quieres ampliar el tamaño de almacenamiento, solo debes modificar la configuración correspondiente.

Gestión de APC

APC dispone además de un entorno web muy útil para ver información sobre el uso que está teniendo el sistema de caché. Para poderlo usar simplemente debes copiar el archivo apc.php a una carpeta que apache pueda tener acceso. El usuario y contraseña para acceder a la página se encuentra dentro del propio archivo php, por lo que es muy fácil de editar y poner los que más te convengan.
Os dejo unas captura de como es actualmente este panel.
Esto ha sido un acercamiento a los sistemas de cache, el opcode y concretamente a APC. Existen otros sistemas de caché y APC puede hacer más de lo explicado en esta entrada, pero se han cubierto sus características básicas.

Actualización:
El archivo apc.php, puede encontrar en las distribuciones ubuntu en la ruta "/usr/share/doc/php-apc/apc.php.gz". Solo se tiene que descomprimir y listo.



miércoles, 14 de marzo de 2012

Patrones de diseño IV - Inyección de dependencias


En la anterior entrada, vimos como crear un adaptador, pero no llegamos a ver como podemos utilizar esos adaptadores para sacar toda su utilidad. Para eso vamos a ver el patrón de inyección de dependencias.

Este patrón nos ayudará a que nuestra aplicación tenga un bajo acoplamiento y una alta cohesión. Para los que no estén familiarizados, decir que eso significa que dos piezas de software no deben ser dependientes entre ellas, pero deben definir correctamente una unidad de software. Para más detalles os dejo una referencia al final de la entrada(1).

Si programas una clase sin utilizar este patrón, la clase resultante puede ser parecida a la del ejemplo, donde se crean objetos dentro de la propia clase y eso hace que haya un fuerte acoplamiento, que es algo que queremos evitar.
class my_class
{
 public function obtenerDatos()
 {
  $base_de_datos = new Mysqli(); //Fuerte acoplamiento

  $base_de_datos->connect();
  $result = $base_de_datos->query( 'SELECT * FROM tabla' );

  return $result;
 }
}

$instancia = new my_class(); 
$instancia->obtenerDatos();

Si utilizamos el patrón, la dependencia se inyecta de forma externa, por lo que la propia clase ya no necesita saber que objeto va a utilizar (realizar un new Objeto), si no solamente la interfaz que va a utilizar de ese objeto, y ahí es cuando entran en juego los adaptadores.

Los adaptadores, tal y como explicamos en la anterior entrada, tienen una interfaz en común que nos asegura que esos objetos tienen un conjunto común de métodos. De esta forma se puede inyectar en la clase cualquier objeto que posea la interfaz que el requiera y olvidarse de que objeto concreto usa.
class my_class
{
 //Inyección de un objeto del tipo InterfazBaseDeDatos
 public function obtenerDatos( InterfazBaseDeDatos $base_de_datos )
 {
  $base_de_datos->connect();
  $result = $base_de_datos->query( 'SELECT * FROM tabla' );

  return $result;
 }
}

$base_de_datos = new AdaptadorMysqli(); //Creamos la dependencia de forma externa.
$instancia = new my_class(); 
$instancia->obtenerDatos( $base_de_datos ); //Inyectamos la dependencia.

En el ejemplo puede verse que la clase ya no requiere el objeto Mysqli, si no que requiere un objeto que tenga la interfaz InterfazBaseDeDatos. Esto permitirá nuestra clase utilizar cualquier objeto que disponga del adaptador.

La dependencia, en el ejemplo anterior, se ha inyectado en el mismo método en el que se utiliza la dependencia externa, pero la inyección de dependencia se puede realizar en otros lugares como por ejemplo el constructor o un método específico para la inyección de dependencias.

Tipos de inyecciones alternativas


Constructor

//Inyección de dependencia en el constructor.
class my_class
{
 //Código de la clase
 public function __construct( InterfazBaseDeDatos $base_de_datos )
 {
  $this->base_de_datos = $base_de_datos;
 }
 //Código de la clase

}

Método específico

//Inyección de dependencia en un método concreto.
class my_class
{
 //Código de la clase
 public function establecerConectorBaseDeDatos( InterfazBaseDeDatos $base_de_datos )
 {
  $this->base_de_datos = $base_de_datos;
 }
 //Código de la clase

}

Por último decir, que si leéis más cosas sobre inyección de dependencias y os encontráis con algo llamado "Contenedor de inyección de dependencias", decir que no es lo mismo que explicado en esta entrada.

Referencias

  1. Acoplamiento y Cohesión

lunes, 5 de marzo de 2012

Patrones de diseño III - Adaptadores

Los adaptadores son un tipo de clase que nos permiten unificar una interfaz para poder cambiar fácilmente entre distintas librerías, haciéndolas así compatibles con nuestra aplicación.

Para ejemplificar este patrón, consideraremos que tenemos una aplicación con la que queremos tener acceso a base de datos con el conector Mysqli, pero queremos dejar la aplicación preparada para poder cambiar de conector fácilmente.

Para empezar a realizar un adaptador, lo primero que tenemos que definir es un interfaz. Pese a que no es un requisito obligatorio, definir un interfaz hará que sea más fácil la implementación del adaptador, evitando así muchos problemas. Para quien no sepa lo que es una interfaz (interface en inglés), le aconsejo que se lea la documentación oficial de php sobre interfaces.

La interfaz que definiremos será un método para poder conectar a la base de datos y otro método para poder ejecutar las consultas y que además nos devuelva su resultado. Un posible código para la interfaz podría ser el siguiente:
<?php
/**
 * Interfaz para la conexión a una base de datos
 */
interface conexionBd
{
        /**
         * Permite al usuario conectarse a la base de datos indicada.
         *
         * @param string $servidor Dirección del servidor.
         * @param string $usuario Nombre de usuario.
         * @param string $contrasena Contraseña.
         * @param string $base_de_datos Nombre de la base de datos a conectar.
         * @return boolean Devuelve TRUE si todo ha ido bien, si no FALSE.
         */
        public function conectar( $servidor, $usuario, $contrasena, $nombre_base_de_datos );

        /**
         * Realiza una petición al motor sql y devuelve el resultado
         * de su ejecución.
         *
         * @param string $sql Petición SQL.
         * @return mixed
         */
        public function peticion( $sql );
}
?>

Como puedes ver, aquí no definimos ningún tipo de implementación, por lo que los métodos están vacíos. Será la clase que implemente esta interfaz, la que deberá programar su lógica.

Ahora que ya tenemos una interfaz común para el acceso a todo tipo de conectores de bases de datos, vamos a crear una clase adaptador que adapte el objeto Mysqli original a  la interfaz genérica que hemos definido.

El código de nuestra clase adaptador es:
<?php
/**
 * Adaptador para Mysqli
 */
class AdaptadorMysqli implements conexionBd
{
        /**
         * Conexión a la base de datos.
         *
         * @var Mysqli
         */
        protected $conexion;

        /**
         * Permite al usuario conectarse a la base de datos indicada.
         *
         * @param string $servidor Dirección del servidor.
         * @param string $usuario Nombre de usuario.
         * @param string $contrasena Contraseña.
         * @param string $base_de_datos Nombre de la base de datos a conectar.
         * @return boolean Devuelve TRUE si todo ha ido bien, si no FALSE.
         */
        public function conectar( $servidor, $usuario, $contrasena, $nombre_base_de_datos )
        {
                $this->conexion = new Mysqli( $servidor, $usuario, $contrasena, $nombre_base_de_datos );
        }

        /**
         * Realiza una petición al motor sql y devuelve el resultado
         * de su ejecución.
         *
         * @param string $sql Petición SQL.
         * @return mixed
         */
        public function peticion( $sql )
        {
                $resultado = $this->conexion->query( $sql );
                if ( $resultado->num_rows > 0 )
                {
                        $datos = array();
                        while( $row = $resultado>fetch_assoc() )
                        {
                                $datos[] = $row;
                        }

                        return $datos;
                }

                return $resultado;
        }

}
?>


Como puedes ver lo primero que hace el código es decir que la clase que implementa la interfaz que hemos definido.
class AdaptadorMysqli implements conexionBd 
De esta forma ahora PHP nos obligará a crear en nuestra clase  todas las funciones que se indiquen en la interfaz que implementa.

En él método de conectar hemos implementado como se realiza la conexión a Mysqli, mientras que en él método de petición hemos implementado una petición más una obtención de resultados. Como ves, no es necesario, ni práctico, que la interfaz se adapte a Mysqli, si no que es al revés, Mysqli debe adaptarse a la interfaz, ya que ahora mismo es Mysqli el que es adaptado, pero al día siguiente puede ser PDO el que quieras adaptar y la misma interfaz te debe servir para cualquiera.

Con todo esto, ya hemos terminado nuestro adaptador pero, ¿esto que nos aporta?. Ahora podemos utilizar en toda nuestra aplicación objetos del tipo conexionBd sin que nos importe que conector a base de datos haya detrás, ya que todos se accederán de la misma forma por la aplicación.

Os dejo el código completo del ejemplo para descargar.