Todo lo que deberías saber sobre APC (II)

Configurando APC para un rendimiento óptimo
Aviso: si no has leído antes el primer artículo de la serie deberías hacerlo.

CONFIGURANDO APC para una óptima optimización optimizada

Como ya sabrás, las directivas de configuración se pueden cambiar en los ficheros .ini de configuración de PHP y sus extensiones. En el caso de Linux, probablemente el fichero esté en /etc/php5/apache2/conf.d/apc.ini, aunque también puedes añadir directivas en php.ini o cualquier otro fichero .ini del directorio conf.d/, ya que todos son cargados por el motor de PHP.

De todas las directivas de configuración que APC ofrece, creo que estas son las más jugosas:

  • apc.stat: indica si APC debe comprobar, a cada petición, si el archivo solicitado ha cambiado. En un servidor de producción es interesante desactivar esta opción ya que nos ahorraremos esa llamada al sistema. Pero ¡cuidado con los cambios de código! Para recompilar un script debes limpiar manualmente el APC o bien reiniciar Apache (con FastCGI no serviría).
  • apc.stat_ctime: comprueba que un archivo ha cambiado a través de los inodes, en lugar de con mtime.
  • apc.shm_size: especifica la memoria reservada para APC, en MB. Por defecto es 30.
  • apc.max_file_size: tamaño máximo de script cacheable.
  • apc.lazy_functions y apc.lazy_classes: cargan las funciones y clases de los includes sólo cuando son necesarios.
  • num_files_hint: aproximadamente cuántos scripts tienes en tu servidor. Ayuda a APC a optimizar sus índices internos. Si no estás seguro, mejor dejarlo a 0.
  • user_entries_hint: aproximadamente cuántas entradas de usuario guardarás. No estás seguro, mejor déjalo en 0.
  • apc.cache_by_defaultapc.filters: si pones apc.cache_by_default = 1, APC sólo cacheará los scripts cuyo nombre coincida con la expresión regular que especifiques en apc.filters. Interesante si tienes algunos scripts que sabes que van a cambiar muy frecuentemente (por ejemplo, durante un período de actualización) y has sido tan machote de desactivar apc.stat.
  • apc.include_once_override: se trata de una característica experimental, que se supone que optimiza las llamadas include_once y require_once para que consuman menos recursos. Dentro de algún tiempo tendremos más información sobre esta funcionalidad.

La extraña pero maravillosa directiva apc.rfc1867

Entre todas estas directivas de configuración destaca rfc1867, que obviamente hace referencia al documento RFC 1867, el cual especifica la subida de ficheros por HTML/HTTP. Pero ¿esto a qué viene?

Pues viene a que con APC, además de cachear los opcodes y guardar valores en la cache, también puedes controlar el progreso de las subidas de ficheros.

Sí, amigos. Si activamos la directiva apc.rfc1867, por cada fichero que se suba a tu servidor APC creará e irá actualizando una user entry con información sobre la subida. Esta entrada contendrá algo así como:

Array
(
    [total] => 1142543
    [current] => 1142543
    [rate] => 1828068.8
    [filename] => test
    [name] => file
    [temp_filename] => /tmp/php8F
    [cancel_upload] => 0
    [done] => 1
)

Es decir, el tamaño total del fichero, los bytes transferidos (current), la velocidad de subida (rate) y otros parámetros. Con esta valiosa información puedes hacer una bonita barra de progreso para indicar a tus usuarios cómo van sus subidas. Si quieres más detalles sobre cómo utilizar esta característica, te remito a este artículo y, como siempre, al manual oficial.

Si te has quedado con ganas de más

  • apc@facebook: presentación de 2007 donde se explica el funcionamiento de APC y se exponen los valores de configuración en los servidores de Facebook. Además explican su interesantísimo sistema para distribuir configuraciones en múltiples servidores a través de APC.
  • 2bits cuentan un caso interesante con Drupal y la discusión es también muy rica.
  • ¡El manual! No me canso de decirlo.

Esto no termina aquí

Te toca a ti. ¿Tienes experiencia con APC? ¿Has notado la mejora de rendimiento en tus servidores de producción? ¿Cómo lo has configurado? ¿Me das la contraseña de root?

Todo lo que deberías saber sobre APC (I)

Una de las formas más fáciles, a la vez que efectivas (un 300% de media), de hacer que tu sitio web PHP sea más rápido es usar un opcode cache. Hay varios en el mercado: APC, eAccelerator, XCache… vamos a ver APC por ser libre, el más fácil de manejar, extendido y el que mejores resultados arroja en los benchmarks.

Pero… ¿qué es un opcode cache?

Diagrama de flujo de APCComo seguramente sabrás, PHP es un lenguaje interpretado. Esto significa que cada vez que se solicita la ejecución de un script (o sea, cuando el usuario navega a una página concreta), este se compila y se ejecuta de forma automática, dando la sensación de que “no hay que compilar PHP”. Es un mito bastante extendido que PHP no se compila. Falso. PHP sí se compila, pero de forma automática. “Just In Time” (JIT), que dicen los anglosajones.

Este sistema de compilación JIT tiene la ventaja de una gestión más simple, sobre todo cuando tienes un script ya funcionando en el servidor y quieres modificarlo. Con PHP simplemente tienes que sustituir el fichero… y listo, la próxima vez que alguien acceda ejecutará la nueva versión.

Pero también tiene una desventaja: a cada visita se debe compilar el script. Y es una pérdida de tiempo, ya que si todos los visitantes de tu web ejecutan el mismo script, ¿para qué compilarlo una y otra vez? ¿No sería mejor compilarlo la primera vez, guardar ese script compilado (opcode) y ejecutarlo cuando alguien lo solicite?

Pues eso es exactamente lo que hacen los opcode cache: compilan el script la primera vez, lo guardan en memoria (RAM), y cuando se solicita, simplemente ejecutan el script compilado, logrando un aumento de velocidad bastante considerable.

Me has convencido. ¡Usemos APC!

APC (Another PHP Cache) es un sistema automático que compila los script sólo cuando es necesario. Viene empaquetado como una extensión de PHP, así que su instalación es sencilla. Y más si estamos usando Ubuntu o Debian Linux:

$ sudo apt-get install php-apc

Con sólo instalar este paquete (suponiendo que ya tengamos instalados Apache y PHP) ya tendremos APC funcionando correctamente. Si quieres más información sobre la instalación de APC puedes ir a la documentación oficial o en Google. Si tu proveedor de hosting no te ofrece APC… lo siento, es lo que tiene el hosting compartido :-P

Para saber si APC está funcionando correctamente, ejecutamos phpinfo():

APC como cache genérico

Además de “cachear” los scripts, APC sirve como almacenamiento genérico en memoria. Esto quiere decir que puedes guardar datos en la memoria de APC que estarán en RAM (por tanto, será de más rápido acceso que si los guardas en el disco duro o en una base de datos) y que podrás recuperar en cualquier momento. Eso sí, recuerda que si reinicias Apache o el sistema operativo, todos los datos que APC haya guardado en memoria se eliminarán.

Para almacenar una variable en la cache de APC puedes usar la función apc_add(), y para leerla, apc_fetch(). Por ejemplo:

/*
 * AVISO: este código tiene fines exclusivamente didácticos, y no debe ser
 * utilizado en aplicaciones reales en producción
 */
 
function leerPerfil() {
    $idUsuario = intval($_GET['idUsuario']);
 
    /**
     * Lee de la BD los datos del usuario $idUsuario. Si los datos están en cache,
     * no hacemos la consulta. Si no están, hacemos la consulta y guardamos los
     * datos en cache
     */
    if (empty($datosUsuario = apc_fetch("perfil_$idUsuario"))) {
        $query = mysql_query("SELECT * FROM usuarios WHERE id = '$idUsuario'");
        $datosUsuario = mysql_fetch_assoc($query);
        apc_add("perfil_$idUsuario", $datosUsuario);
    }
 
    return $datosUsuario;
}

Este sistema te permite hacer optimizaciones adicionales en tus aplicaciones (por ejemplo, guardar el resultado de una consulta a la base de datos para no tener que hacerla dos veces), pero tendrás que currártelas a mano. Tú eliges qué almacenar en APC. Pero ojo, si vas a hacer un uso intensivo de la caché, es mejor que uses un sistema de caché diferente al de los scripts. Por ejemplo, el famoso memcached (usado por sitios como Google o Facebook), que es un sistema de caché genérico, muy eficiente y muy fácil de usar (en serio, es muy fácil). Más información sobre funciones de APC en el manual de PHP.

Monitorizar APC

Otra de las ventajas de APC es que tiene un precioso panel de control. Es un script PHP al que puedes acceder haciendo un enlace desde tu directorio www público hasta la ubicación del script (en los directorios del sistema de PHP):

gunzip -c /usr/share/doc/php-apc/apc.php.gz > /var/www/apc.php

Si no funciona, o si no usas Linux puedes leer la documentación para instalar el panel de control de APC. Si no encuentras el script en tu sistema de ficheros puedes sencillamente descargarlo desde el Subversion de APC.

Aviso de seguridad: este script permite ver el estado de APC, revelando alguna información sobre el servidor que puede ser sensible por temas de seguridad. Es tu responsabilidad proteger el acceso al panel de control a través de configuraciones del servidor web, etc.

El panel de control tiene algunas opciones “públicas” y otras a las que sólo puedes acceder iniciando sesión con nombre de usuario y contraseña. Para cambiar el nombre de usuario y la clave puedes editar el propio script del panel de control, apc.php:

Cambiar la contraseña al panel de control de APC

El panel de control nos ofrece tres apartados:

  1. View Host Stats, donde tenemos un cuadro de control de APC con gráficas de consumo de memoria, información del sistema y datos de uso del cache.
  2. System Cache Entries: la lista de scripts cacheados, así como el número de hits (ejecuciones) que han tenido, su tamaño y fechas de último acceso/última modificación/creación.
  3. User Cache Entries: las variables que se hayan guardado en APC.

En el próximo artículo explicaremos más detalles sobre el panel de control de PHP y cómo interpretar los datos que nos brinda de cara optimizar aún más nuestro sitio web. Aunque si has llegado hasta aquí y has instalado APC en tu servidor ya has conseguido un gran ahorro de recursos en tu servidor :-)

Os presento a Kanpress

¡Hola! Estas últimas semanas he estado desarrollando una idea que surgió de una necesidad real: coordinar el trabajo de los cerca de diez redactores que escriben en Caminayven –se trata de una revista católica que funciona con WordPress–. Si nuestro trabajo es escribir artículos y un artículo puede estar en estado propuesto, en desarrollo o pendiente de revisión… ¿por qué no usar la metodología Kanban?

Así surgió la idea de Kanpress, un plug-in de WordPress que acerca los tableros Kanban al mundo editorial. Un artículo, una tarea. Proponer, asignar, desarrollar, revisar. Si quieres saber más sobre Kanpress, te remito a la página del proyecto. Aquí contaré brevemente cómo está siendo la gestión de un proyecto de software libre non-profit, pequeño pero con vocación de crecer en usuarios y características.

El comienzo del proyecto fue sencillo: la idea, el prototipo sobre papel y una implementación rápida de las funcionalidades más básicas: crear tarea, asignarla, mover su estado, etc. En cuanto tuve una primera y muy verde versión beta, la subí a Caminayven para que los compañeros la probaran, detectando errores y recibiendo de ese feedback la priorización de las siguientes funcionalidades a implementar.

Boceto de Kanpress en papel

El siguiente hito fue la Betabeers, reunión de programadores en Murcia, que aproveché para presentar Kanpress a un selecto grupo de espartanos que acogieron la idea con mucho interés. Para poder llegar al día de la presentación con algo decente trabajé en un sprint de dos días dedicados casi en exclusiva a mejorar la interfaz y pulir errores. Dos días en los que, por cierto, la técnica del pomodoro tuvo una importancia capital.

Es destacable el dato de que, cuando presenté Kanpress en la Betabeers (el pasado 8 de febrero), ni siquiera traía las rutinas de instalación necesarias para que el público pudiese probar Kanpress. Sólamente funcionaba en mi portátil y en caminayven.com. Pero funcionaba y tenía una interfaz decente, lo que me permitió causar buena impresión y captar algunos colaboradores. Pragmatismo en estado puro.

Cuando se celebró la Betabeers era demasiado pronto para que Kanpress rompiera el cascarón, pero las oportunidades hay que aprovecharlas. Los días siguientes fueron más de documentar y preparar Kanpress para su distribución que de agregar nuevas funcionalidades, así como de darle un poco de bombo (algunos blogs se hicieron eco de la existencia de Kanpress).

El tercer hito fue enviar Kanpress al repositorio oficial de plug-ins de WordPress, que fue aceptado al día siguiente. Esto suponía, principalmente, dos cosas: que Kanpress tenía que hablar inglés, y que debía usar el repositorio Subversion que me proporcionaba WordPress (y esto era un problema porque desde el principio trabajé con GitHub).

Así que últimamente me he esforzado por internacionalizar Kanpress (inglés, español y gallego son los idiomas soportados actualmente) y he decidido dejar de usar GitHub, ya que no he conseguido de forma elegante sinronizar ambos repositorios. También he procurado hacer completa y atractiva la página del plug-in en WordPress.

En resumen:

  • Implementa la idea lo más rápido que puedas y pide opinión a gente (mejor si es gente que entienda).
  • Pónselo fácil a los usuarios: haz una buena página, bonita y atractiva.
  • Internacionaliza: seis mil millones de cabezas piensan más que 300 millones de hispanohablantes.

¿Algún comentario? ¿Gestionas algún proyecto de software libre? ¡Comparte tu opinión en los comentarios!

PUFW8V3HXSQF

Mirando con perspectiva

Great web engineers are generalists. To understand the web, you need to know a lot more than how to render variables in templates or pul objects from an ORM. You need to have spent time learning about networking, software architecture, web protocols and standards, graphic design, user interfaces, and TODO. Most of all, you need to be passionate about the web and the amazing ways it’s transforming our lives, nearly 20 years after HTTP and HTML were designed.

Visto en… ¡una oferta de trabajo!

Modelos experimentales de persistencia para aplicaciones web (II): Object Freezer-Relational

En el anterior post vimos un invento de Sebastian Bergmann llamado Object Freezer, que consiste en almacenar objetos PHP en CouchDB de forma automática. Es un buen método para no tener que definir esquemas de bases de datos, escribir sentencias SQL, hacer mapeo objeto-relacional… en definitiva, es una solución que nos puede ahorrar un montón de trabajo.

Pero, una vez aceptadas las ventajas de la congelación, quizá sea necesario guardar esos objetos en una base de datos diferente a CouchDB. Por ejemplo, en MySQL. Y de esto trata el presente artículo: de congelar objetos y guardarlos en MySQL.

Un brevísimo repaso a la congelación

Congelar un objeto (“freeze”) es convertirlo en un array manteniendo los objetos hijos y evitando duplicidad de los mismos. La operación inversa, la descongelación (“thaw”), sería recoger ese array y convertirlo de nuevo en el objeto original.

¿Por qué MySQL, si con NoSQL nos ahorramos todo el trabajo?

  1. A veces no es viable o rentable cambiar de servidor de bases de datos, por la inversión económica y de formación que requieren los sistemas tradicionales.
  2. Interoperabilidad: si en el futuro queremos migrar la aplicación, quizá poder manipular la información con SQL facilite la tarea.
  3. Rendimiento: las bases de datos relacionales se basan en algoritmos muy evolucionados y rápidos, y sistemas como MySQL-MyISAM han demostrado un rendimiento muy alto.

Entonces… ¿Se trata de guardar arrays en MySQL?

Exacto. Eso es lo que hace Object Freezer-Relational, una extensión de Object Freezer escrita por un servidor :-)

Lo primero que debemos pensar a la hora de guardar objetos en MySQL es: ¿cuál es el esquema de tablas? Porque si con CouchDB éramos NoSQL y guardábamos la información en arrays JSON, ahora tenemos que ceñirnos al álgebra relacional, las tablas, los índices y las claves primarias.

Así que este es el esquema que podemos usar:

  • Tabla objects
    • id: el ID único del objeto.
    • className: indica la clase de la cual es instancia el objeto almacenado.
    • isDirty y isRoot: dos atributos que utiliza Object Freezer internamente.
  • Tabla properties
    • name: nombre del atributo.
    • value: contiene el valor del atributo o bien un ID si el atributo hace referencia a otro objeto.
    • type: tipo de dato (int, float, string, boolean, array, objeto…).
    • object_id: ID del objeto al que pertenece el atributo. Hace referencia a objects.id.

Llegados a este punto uno podría pensar: “hey, te estás cargando el modelo relacional”. Efectivamente. Si queremos guardar objetos sin definir su esquema de BD, no nos queda más remedio que soluciones experimentales como la que te presento hoy. Sí, es una “des-normalización”, pero gracias a ella te ahorras escribir SQL.

Object Freezer-Relational se encarga de gestionar este esquema. De hecho, no es siquiera necesario crearlo, ya que lo hace automáticamente.

Vale, ya veo que mola. ¿cómo se usa?

Usar Object Freezer-Relational es tan sencillo como usar Object Freezer indicando los datos de acceso a MySQL:

$storage = new Object_Freezer_RelationalStorage(
    new Object_Freezer,
    NULL,
    FALSE,
    new MysqlStorage(
        "localhost",                    //Servidor MySQL
        "freezer",                      //Usuario
        "passw0rd",                     //Contraseña
        "freezer",                      //Base de datos
        3306,                           //Puerto
        MysqlStorage::ENGINE_INNODB));  //Motor MySQL

A partir de ahí, podemos usar el objeto $storage de la misma forma que el Object Freezer original, con CouchDB, ya que es la misma API.

Object Freezer-Relational está disponible en SourceForge. Puedes descargarlo y probarlo, echar un vistazo al código o leer la documentación si te interesa. Y por supuesto, comentar qué te parece la idea de guardar objetos en una base de datos MySQL :-)