Archivo del sitio

[apache] benchmark de diversas configuraciones de Apache+PHP

He estado configurando un load-balance de servidores Apache2; claro, usando nginx como proxy reverso, una de las dudas que me saltó durante las pruebas fue:
¿cómo lograr el máximo rendimiento en cada nodo?, y es que, cada nodo de apache2 se ejecuta en una máquina virtual con una ínfima cantidad de RAM, así que, manos a la obra, decidí tomar el control del asunto.

Apache y MPM

Los “módulos de multi-procesamiento” (“Multi-Processing Modules” ó MPM) es como llama apache a sus módulos que procesan las solicitudes.

mpm_prefork: el más antiguo y utilizado por usuarios de PHP, prefork genera procesos de apache con el interprete PHP imbuido (mod_php), es el más sencilo de configurar (apt-get install apache2-mpm-prefork libapache2-mod-php5); sin embargo, prefork tiene el inconveniente de tener que integrar un compilador de PHP en cada proceso, por consiguiente el consumo de recursos ante sitios con alta demanda, es excesivo.

mpm_worker: en worker no sólo se generan procesos, sino que cada proceso puede generar múltiples hilos, al procesar múltiples hilos de manera asincrónica a la vez, podemos consumir menos recursos que levantar un proceso de apache por cada solicitud

mpm_event: es una versión más reciente de worker, los procesos padre generan hilos “principales”, dichos hilos principales pueden “pasar” procesos a hilos hijos (creados por demanda) para asumir nuevas solicitudes, event tiene una característica de trabajo muy parecida al modo asíncrono multi-hilado de Nginx.

Las pruebas:

Hemos instalado el siguiente servidor (una máquina virtual) muy sencilla:

Servidor: Debian Wheezy 7.0
RAM: 512MB
Document Size: 70Kb
CPUs: 1

Usando apache benchmark, con una configuración muy simple:
ab -n 100 -c 50 http://web1.example.net/test.php

Donde test.php contiene un “loop” sencillo y un phpinfo().

He decidido probar las diferentes combinaciones tomando el mejor de 10 resultados.

Resultados (raw)

* apache mpm_prefork + mod_php:

la clásica combinación MPM_PREFORK+MOD_PHP.

Server API: Apache 2.0 Handler

Concurrency Level: 50
Time taken for tests: 37.756 seconds
Complete requests: 100
Failed requests: 14
 (Connect: 0, Receive: 0, Length: 14, Exceptions: 0)
Write errors: 0
Total transferred: 6701286 bytes
HTML transferred: 6674386 bytes
Requests per second: 2.65 [#/sec] (mean)
Time per request: 18877.780 [ms] (mean)
Time per request: 377.556 [ms] (mean, across all concurrent requests)
Transfer rate: 173.33 [Kbytes/sec] received

* apache mpm_worker + php5-fpm + fastcgi:

hemos combinado PHP5-FPM (vía socket, es unos 15% más rápido así que vía TCP, ya que te quitas unas cuantas capas del transporte TCP/IP) + fastcgi (el modo por defecto de configurar FPM, ya que fcgid no permite enviar solicitudes a procesos remotos).

Server API FPM/FastCGI

Concurrency Level: 50
Time taken for tests: 33.945 seconds
Complete requests: 100
Failed requests: 5
 (Connect: 0, Receive: 0, Length: 5, Exceptions: 0)
Write errors: 0
Total transferred: 7714795 bytes
HTML transferred: 7680695 bytes
Requests per second: 2.95 [#/sec] (mean)
Time per request: 16972.487 [ms] (mean)
Time per request: 339.450 [ms] (mean, across all concurrent requests)
Transfer rate: 221.95 [Kbytes/sec] received

Nota: el uso de RAM es muy eficiente usando php5-fpm, nunca consumiendo más de 140MB de RAM.

* apache mpm_worker + php5-cgi + mod_ fcgid:

combinamos mpm_worker con PHP5 en modo fastCGI pero usando fcgid

Server API CGI/FastCGI

Concurrency Level: 50
Time taken for tests: 32.200 seconds
Complete requests: 100
Failed requests: 8
 (Connect: 0, Receive: 0, Length: 8, Exceptions: 0)
Write errors: 0
Total transferred: 7641087 bytes
HTML transferred: 7606987 bytes
Requests per second: 3.11 [#/sec] (mean)
Time per request: 16099.806 [ms] (mean)
Time per request: 321.996 [ms] (mean, across all concurrent requests)
Transfer rate: 231.74 [Kbytes/sec] received

* apache mpm_event + php5-cgi + mod_fcgid:

realizamos la misma combinación, pero sustituyendo mpm_worker por mpm_event.

Server API CGI/FastCGI

Concurrency Level: 50
Time taken for tests: 46.755 seconds
Complete requests: 100
Failed requests: 8
 (Connect: 0, Receive: 0, Length: 8, Exceptions: 0)
Write errors: 0
Total transferred: 7641191 bytes
HTML transferred: 7607091 bytes
Requests per second: 2.14 [#/sec] (mean)
Time per request: 23377.675 [ms] (mean)
Time per request: 467.553 [ms] (mean, across all concurrent requests)
Transfer rate: 159.60 [Kbytes/sec] received

* apache mpm_event + fastcgi + php5-fpm:

La misma combinación de fastcgi + php5-fpm, pero usando mpm_event

Server API FPM/FastCGI

Concurrency Level: 50
Time taken for tests: 35.976 seconds
Complete requests: 100
Failed requests: 10
 (Connect: 0, Receive: 0, Length: 10, Exceptions: 0)
Write errors: 0
Total transferred: 7714787 bytes
HTML transferred: 7680687 bytes
Requests per second: 2.78 [#/sec] (mean)
Time per request: 17987.970 [ms] (mean)
Time per request: 359.759 [ms] (mean, across all concurrent requests)
Transfer rate: 209.42 [Kbytes/sec] received

Nota: se incrementó hasta 400 conexiones con 100 niveles de concurrencia, los valores de conexión y procesamiento se mantuvieron invariables.

 

Las gráficas

… y es que la gente le encanta un gráfico

conexion

El primero representa el tiempo que tarda Apache en recibir una conexión y procesarla, como vemos, aun con pocas solicitudes, prefork se va **adelante** (pues el servidor con solo 512MB de RAM estaba a punto de irse a la SWAP), mientras, mpm_event fue más diligente en conectarse y procesar rápidamente las solicitudes, sin tomar en cuenta cuanto más tardó en transmitirlas.

Ganador: Empate entre MPM-Event+PHP5-FPM y MPM-Event+PHP5-CGI+FCGID

tiempo-total

El tiempo total de la prueba, es lo que tardó Apache desde que se inició el apache-benchmark hasta que se completaron las 100 solicitudes, como vemos (y muy interesante el resultado) event-fcgid fue el peor en el resultado, tardando incluso más que mpm_prefork en conectar-procesar y transmitir resultados, de manera paradójica, la misma configuración pero usando mpm_worker fue la mejor de todas.

Ganador: MPM-Worker+PHP5-CGI+mod_fcgidsolicitud

En general, los resultados fueron parejos, sin embargo, worker-fcgid aparece nuevamente victorioso acá, además, en promedio terminó más solicitudes por segundo (unas 3.11 solicitudes/seg versus 2.9 de worker-fpm).

Ganador: MPM-Worker+PHP5-CGI+mod_fcgid
transfer-rate

 

La velocidad de respuesta, o rata de transferencia de los datos desde el servidor hacia el cliente, es también muy importante, como vemos, Worker-fcgid aparece con una velocidad más alta que el resto.

Ganador: MPM-Worker+PHP5-CGI+mod_fcgid

… ¿Y por qué no Nginx?

Con un conjunto de VPS, uso de .htaccess, facilidad de implementación de instalaciones adaptadas a apache2, en uso de apache2 es *casi* que una obligación en el proyecto que estoy realizando, sin embargo, es más que notable la diferencia de Nginx+PHP5-FPM versus el resto de configuraciones.

Pero para este caso en particular, necesitaba apache2.

Las conclusiones

Es precipitado adelantarse a dar ganadores sin explicar para qué se podría utilizar cada combinación, por ejemplo, mpm_prefork se muy sencillo de configurar y para sitios web con muy pocas visitas y si el recurso no es problema, con APC (o xcache) más mod_deflate+mod_expires puede superar a cualquiera de las configuraciones actuales.

Para sitios web con muchísimo tráfico y tiempos de respuesta cortos, es obvio que MPM-Worker+PHP5-CGI+mod_fcgid podría rendir mejor que cualquier otra combinación (MPM-Event+PHP5-FPM+FastCGI podría ser una segunda opción); sin embargo, hay que recordar que mod_fcgid no permite llamar procesos externos (como PHP5-FPM).

PHP5-FPM aparece como una buena opción (en conjunto con MPM-Worker, aunque mejores números, MPM-Event suele consumir más recursos como RAM y CPU que Worker) para pequeños servidores con bajos recursos, PHP5-FPM es bastante óptimo (durante las pruebas Apache+PHP-FPM+todo el sistema operativo no consumían más de 150MB de RAM), además la posibilidad de “compartir” el proceso PHP5-FPM con otras instancias (de apache o de nginx) tanto en el servidor como en otros servidores, nos brindan un ahorro notable de recursos.

La gran sorpresa de la noche fue mpm_event, tal vez me faltó algún detalle de configuración, sin embargo, ver que MPM_EVENT consume más recursos que mpm_worker y además, no rinde mejor que este … me hace dudar de implementarlo con PHP.

[PHP5] Obtener el SID de un dominio en modo nativo con ldap-toolkit

Fuente: https://code.google.com/p/php5toolkits/wiki/ObtenerSid

Obtener el SID de un Dominio Active Directory es parte *esencial* de una migración exitosa de un dominio MS a Samba; ya que este representará el SID del nuevo dominio.

Extraerlo usando ldap-toolkit es cosa bastante sencilla; como lo explico en el wiki del proyecto.

Configurar una conexión

Para usar una conexion a un MS Active Directory, configuramos:

#una conexion usando un Active Directory Service
$database['ads']['adapter']           = 'ads';
$database['ads']['host']              = '10.1.1.1';
$database['ads']['basedn']            = 'DC=test,DC=com,DC=ve';
$database['ads']['domain']            = 'test.com.ve';
$database['ads']['netbios_name']      = 'TEST';
$database['ads']['username']          = 'administrator';
$database['ads']['password']          = 'mipassword';
$database['ads']['port']              = 389;
$options = array('LDAP_OPT_PROTOCOL_VERSION' => 3, 'LDAP_OPT_REFERRALS'=>0, 'LDAP_OPT_SIZELIMIT'=>5000, 'LDAP_OPT_TIMELIMIT'=>300);
$database['ads']['options']           = $options;

#Importante el usuario y el netbios_name (nombre del dominio netbios) para el login

Snippet de Codigo

include "conf/base.inc.php";
include_once BASE_DIR . "conf/include_ldap.inc.php";

#obtenemos la configuracion AD
$ldap = ldap::load('ad');

#buscar una entrada basica, para extraer su SID
$entry = "(&(objectClass=user)(samaccounttype=". ADS_NORMAL_ACCOUNT .")(samaccountname=jesuslara))";
#conectamos al AD
$ldap->open();
$entry = $ldap->query($entry);
echo "SID usuario {$user}: " . $entry->bin_to_str_sid('objectSid');
$ldap->close();

Ese echo, mostrará un valor semejante a: ‘S-1-5-21-2102913520-367280043-1452191782-14800′ De esa salida: S-1-5-21-2102913520-367280043-1452191782-14800

Lo que está en negrillas es el SID del dominio, lo que es itálica, es el UidNumber del usuario; simplemente buscar la ubicación del último “-” y todo lo que hay desde ‘0’ hasta strpos(‘-‘) del último guión será el SID del dominio.

Fácil, rápido y permite una migración más transparente de dominios nativos MS AD a Samba.

PHP5 – TDOM: Creando archivos XML usando DOM y PHP

En este post presento TDOM (Tomato:Document Object Model), una librería que permite simplificar el proceso de leer, analizar, modificar o crear archivos XML-based de distintos tipos usando una API simplificada y utilitaria.

Aunque esta librería tiene ya bastante tiempo conmigo (a menos más de un año desde que la hice); un “re-factoring” para hacerla más compatible con PHP > 5.3 y la posibilidad de tener una interfaz basada en “plugins” que permiten extender sus capacidades agregando nuevos tipos de documentos XML y además de crearle un proyecto libre en Google Code; es lo que me hace escribir el articulo.

El proyecto nace bajo licencia GPL v.3 y está en fase alpha, versión 0.1.2; permite crear documentos y archivos en:

  • XML
  • DocBook
  • SVG
  • (x)HTML estricto
  • Interfaces XUL remote

Espera incorporar próximamente (ya estoy programando en ello):

  • XMPP (Paquetes del protocolo XMPP que usan Jabber/Gtalk)
  • RSS (Sindicación de manera fácil)
  • RDF (Resource Description es base de muchos tipos de archivos, como FOAF: relaciones amigo-amigo descritas semánticamente usando RDF)
  • ATOM
  • ODF

TDOM forma parte de la capa de creación y vistas de Tomates Framework, que se encuentra en un proceso final de adaptación y re-invención; lo importante de TDOM es que permite crear interfaces XUL, combinado con Jquery+REST sería el primer framework completamente operativo sobre PHP (el otro es Cyclone en perl) que permitiría crear aplicaciones XUL:Remote (que correrían como una aplicación desktop usando XULRunner) en implementar completamente Interfaces XUL de Mozilla.

Creando una Interface XUL

Está claro que el código dentro de poco será más simplificado cuando todos los widgets UI (toolbars, menu, tabbers, botones) estén incorporados en la clase; pero este es un ejemplo escribiendo solo PHP:

$xml = $dom->type('xul');
#un tabbox
$tabbox = $xml->create('tabbox');
$tabbox->orient('vertical')->flex('1');
$tabs = $tabbox->create('tabs');
$tabpanel = $tabbox->create('tabpanels');
$tabpanel->flex('1');
#adjunto las pestañas:
$tabs->create('tab')->label('Google');
$tabs->create('tab')->label('PHP.net');
$tabs->create('tab')->label('DEVEL');
#y el contenido de las pestañas
$tabpanel->create('browser')->src('http://www.google.co.ve/');
$tabpanel->create('browser')->src('http://www.php.net/');
$tabpanel->create('browser')->src('http://www.devel.com.ve/');
$xml->title('Prueba de XUL');
$dom->render(); 

Output:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin/"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="xulWindow" persist="screenX screenY width height sizemode" xmlns:html="http://www.w3.org/1999/xhtml" title="Prueba de XUL">
  <tabbox orient="vertical" flex="1">
    <tabs>
      <tab label="Google"/>
      <tab label="PHP.net"/>
      <tab label="DEVEL"/>
    </tabs>
    <tabpanels flex="1">
      <browser src="http://www.google.co.ve/"/>
      <browser src="http://www.php.net/"/>
      <browser src="http://www.devel.com.ve/"/>
    </tabpanels>
  </tabbox>
</window>

Proximamente subiré una demo de como se ve esta interfaz XUL.

Creando un archivo (x)HTML estricto

Un ejemplo de como el código puede ser simplificado, es tener todo un cuerpo XHMTL 1.0 estricto teniendo solo 3 líneas de código; esto gracias a que todas las etiquetas son necesarias para la descripción de un archivo según la norma de la W3c; estas 3 líneas:

$html = $dom->type('html'); 
$html->setTitle('.: Titulo de la Pagina Web :.');
$dom->render();

Generan todo este bloque de código que es sintácticamente válido con XHTML 1.0 estricto; lo interesante es que podemos cargarle contenido al BODY con TDOM y este será normalizado para ser XHTML 1.0 estricto; básta de aplicaciones inválidas.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://localhost/proyectos/tomates/tomates/include/tdom/tomates/tdom/include/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>.: Titulo de la Pagina Web :.</title>
    <base href="http://localhost/proyectos/tomates/" />
<!--Aqui comienzan las etiquetas meta-->
    <meta name="author" content="" />
    <meta name="generator" content="Tomates Framework" />
    <meta name="description" content="" />
    <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
    <meta http-equiv="Content-Script-Type" content="text/javascript" />
    <meta http-equiv="Content-Style-Type" content="text/css" />
    <meta name="robots" content="all" />
    <meta http-equiv="Content-Language" content="en_US" />
    <meta name="keywords" content="" />
    <meta name="Revised" content=": Sep 18 2009" />
<!--Aqui terminan las etiquetas meta-->
    <link rel="shortcut icon" href="http://localhost/proyectos/tomates/application/assets/images/favicon.ico" type="image/x-icon" />
    <link rel="start" href="http://localhost/proyectos/tomates/" />
    <link rel="index" href="http://localhost/proyectos/tomates/" />
  </head>
  <body></body>
</html>

Creating an (x)HTML strict

Code:

                $html = $dom->type('html');
                $html->createHead();
                $html->setTitle('.: Titulo de la Pagina Web :.');
                $dom->render();

Output:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://localhost/proyectos/tomates/tomates/include/tdom/tomates/tdom/include/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>.: Titulo de la Pagina Web :.</title>
    <base href="http://localhost/proyectos/tomates/" />
<!--Aqui comienzan las etiquetas meta-->
    <meta name="author" content="" />
    <meta name="generator" content="Tomates Framework" />
    <meta name="description" content="" />
    <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
    <meta http-equiv="Content-Script-Type" content="text/javascript" />
    <meta http-equiv="Content-Style-Type" content="text/css" />
    <meta name="robots" content="all" />
    <meta http-equiv="Content-Language" content="en_US" />
    <meta name="keywords" content="" />
    <meta name="Revised" content=": Sep 18 2009" />
<!--Aqui terminan las etiquetas meta-->
    <link rel="shortcut icon" href="http://localhost/proyectos/tomates/application/assets/images/favicon.ico" type="image/x-icon" />
    <link rel="start" href="http://localhost/proyectos/tomates/" />
    <link rel="index" href="http://localhost/proyectos/tomates/" />
  </head>
  <body></body>
</html>

Map / Reduce en PHP comparado con Scheme

PHP6 será compatible con funciones lambda y agregará por ende algunas caracteristicas de lenguajes funcionales; interesante es entonces encontrar entradas como esta, que nos explica de una manera simple y sencilla la implementación de una “high-order function” en PHP6 comparandola luego con Scheme (un lenguaje funcional).

Veamos el ejemplo comparando la función MAP en PHP6 y en Scheme:

function map($fn, $list) { 
  return empty($list) ?
  array() :
  construct($fn(first($list)),
  map($fn, rest($list)));
}

Y ahora vemos la misma función equivalente en scheme:

(define (map fn lis)
 (if (null? lis)
 '()
 (cons (fn (car lis))
 (map fn (cdr lis)))))

Las funciones anónimas solventan muchos problemas que antes se solventaban con loops recursivos bastante peligrosos; que vamos a estar claros no aporta la potencia de Lisp o Scheme, pero ya es un avance en el lenguaje.

[PHP 6 Namespaces] Una mala implementación

En las primeras de cambio, cuando aquel ruso Dimitri (Dmitri Vinogradov) postuló el parche para soporte de namespaces en php5 ó 6 y se inició un rally para incorporarlos rápidamente al lenguaje; mucha gente se entusiasmó, pensando que por fín se acabaría la cadena (o el yugo) a los nombres de función crípticos y a las implementaciones de 40 mil includes y requires al inicio de un archivo.

El preámbulo

Inicialmente el parche prometía bastante; al implementar un sistema de espacio de nombres basado en filesystem (y con soporte y resolución dinámica) muy semejante al de python:

<?php
namespace Proyecto::DNS;
class resolver {
public function get($ptr) {
}
}
?>

Y su implementación se realizaba:

<?php
use Proyecto::DNS as dns;
dns->get('127.0.0.1');
?>

Lo cual por legibilidad es más o menos como usar:

import DNS
DNS.get('127.0.0.1')

Lo cual parecía ser la solución “idónea” a los problemas de resolución del lenguaje; sin embargo, en octubre del 2008 la gente del buró de decisiones de Zend (la empresa que lleva el lenguaje PHP); decidió, fuera de toda lógica plausible; tratar a los espacios de nombres y a las clases como dos cosas completamente distintas; de tal manera que la resolución de nombres usando el :: quedaba descartada del lenguaje.

El problema

En la mayoría de los lenguajes no encontrarás una clase llamada igual que un namespace (o package como en Java o módulo como en python); o sea, nunca verás algo como:

import org.java.lang

donde Java.lang pueda ser un package y además una función; los increíbles de PHP desean hacer eso; con lo cual sentenciaron el separador (::) a muerte (al menos como separador de los namespaces, ya que el punto (como en python o java) no se puede usar porque es el concatenador lógico)).

Según la gente de Zend; tener:

<?php
namespace Proyecto;
function DNS() {}
?>

y

<?php
class Proyecto {
public function DNS() {
}
}
?>

Es sintáctica y además lógicamente válido;  lo que conlleva a que:

<?php
Proyecto::DNS();
?>

No sepa determinar si nos referimos a la función DNS en el namespace Proyecto o al método DNS en la clase Proyecto, llevando a confusiones al motor Zend2; obviamente, esto conllevó a una decisión.

La fatídica solución (Según Zend)

Lo que la gente de Zend ha ideado; es obviamente un disparate; como según ellos lo anterior “se puede hacer” (aunque no se debería tener un namespace y una clase con el mismo nombre), entonces debemos “adoptar” el slash (o separador Windows “\”, que se alegren los programadores .NET) como separador de las resoluciones de nombre; de tal manera que lo de arriba queda así:

<?php
Proyecto::DNS(); //llamando estáticamente al método DNS de la clase Proyecto
\Proyecto\DNS(); //llamando a la función DNS del namespace proyecto
?>

¿Que rayos es esa cosa?; otra horrible invención de Zeev Zurasky y pandilla!; esto hará que:

<?php
\Date\time(); //namespace Date, función time()
Date::time(); //clase incorporada Date, método time()
?>

Es decir, a la clase Date (incorporada en PHP5.1) puedo crear paralelamente un namespace Date con una función llamada idénticamente a su contraparte de la clase; no hay nada más horrible que esto.

¿No era más sensato, como en otros lenguajes, evitar que un package y una clase compartieran el mismo nombre?.

Las ventajas (que no lo son tanto)

He decidio copiar y desmentir cada una de las “ventajas” que han puesto para aceptar el cambio del separador.

  • name ambiguity is impossible
¿Qué es más ambiguo?, que se “lean” diferente o que pueda existir un Namespace y una clase con el mismo nombre?; imaginen esto:
<?php
\com\Zend\Data\Form::validate();
\com\Zend\Data\Form\validate();
?>
Ambas formas en el mismo script!, sintáctica y logicamente válidas; pero a la vista, ambiguas ambas (y un completo disparate).

  • \ is visually quite different from ::, and is easier to scan and detect as namespace instead of static class operator

Creo que la presencia de un import (o USE en PHP) es una facil forma de detectar “visualmente” quien es un namespace y a quien nos referíamos estáticamente.

  • \ is a single keystroke on U.S. keyboard layout without shift key

En la mayoría de los teclados latinoamericanos y españoles es imposible obtener el \ a la primera, a veces uno ni lo consigue!.

  • \this\is\used for paths on Windows and is intuitively familiar to those developers. According to a php|arch survey (as relayed by Steph Fox), most of their readers develop on Windows and deploy on Unix, which would imply that \these\paths are familiar

… Sin comentarios; recordemos que ahora Microsoft le apuesta a los lenguajes de scripting libres (python, php) en vez de a su .NET, así que tiene una fuerte inversión con Zend.

  • \this\maps\to\filesystem layouts often used by autoload intuitively for the above reason

Pudieron haber escogido el backslash de unix igualmente, o el punto, para parecerse más a otros lenguajes “serios” (ruby, phyton) …

  • because \ is a single keystroke, it is possible to require \ prefix for all global functions/classes/constants, and conversion is minimally effortful (from personal experience trying to convert files to several namespace separators to see what it was like)
El “namespace” global requerirá que “escapemos” todas las funciones y constantes globales, de tal manera que tendremos que llenar el código con cosas como:
<?php
\htmlspecialchars($variable);
?>
Simplemente hilarante.
  • code review ambiguities disappear permanently
Y muchos programadores enojados desaparecerán también permanentemente.
El Análisis posterior
Este parche ha sido incorporado a PHP 5.3; pero aún los cambios no han sido incorporados a PHP6; muchos vaticinan que ocurrirá algo parecido a la actual página http://www.gophp5.org (un gophp6.org) en un tiempo no mayor de 3 años; como consecuencia de que “proyectos de PHP4 no correrán en > PHP 5.3″; pero además, código escrito en PHP 5.3 o superior posiblemente no corra en PHP6.
Esto hace además que guias como esta; publicadas antes del parche de noviembre del 2008, sean inválidas, dicho sea de paso, hasta el .chm oficial en español de la documentación de PHP está erroneo pues no ha incorporado los cambios.

¿Podemos tener PHP5 corriendo en un apache2 worker?

La respuesta básica a esta pregunta es sí; la razón de por qué no es tan simple, pero trataré de explicarlo en el siguiente artículo.

Apache 2 con mod_php puede ser ejecutado en dos modos; mpm_prefork y mpm_worker; mpm_prefork es una especie de compatibilidad con apache 1.x y es bueno para aplicaciones que poseen módulos que no son thread-safe; mpm_worker es un sistema multi-proceso multi-threaded (multi-hilado) y requiere que las aplicaciones en él ejecutadas posean hilos seguros de ejecución (thread-safe); mpm_worker es muchisimo más estable y puede ofrecer más conexiones con menos consumo de recursos; además de ser el ideal para equipos multi-core o multi-procesador.

mpm_worker tiene un rendimiento notablemente superior a mpm_prefork en la gran mayoría de las condiciones generales de un servidor web: ver aquí.

Entonces; ¿Por qué se usa mpm_prefork para PHP5? …

Muchas extensiones php5 (y php4) no son thread-safe; esto hace que php5 sea imposible de usar en modo mpm_worker sin “castrarlo” retirando aquellas extensiones que no lo son; sin embargo, esto no es problema alguno, puesto que la gran mayoría de las extensiones no “thread-safe” son básicamente no usadas por la mayoría; un ejemplo es la extensión mm; que permite crear una caché compartida para gestionar las sesiones (y que puede seguramente ser reemplazada por memcached) casi nunca es usada; gd versión 1 debe ser evitada y usar gd2.

Otro mito extendido por esto es que php5 no puede ser ejecutado en modo worker; esto es porque en Debian solo están los binarios compilados en modo prefork y de hecho, instalar php5 desinstala apache2 mpm_worker e instala mpm_prefork; por lo que he decidido compilar mi propio php5 para Debian Lenny (apache 2.2.4 y php 5.2.5) con soporte mpm_worker.

Compilando php5

Primero, debemos instalar algunas librerías que son requeridas (devel) para compilar php5:

>aptitude install libmhash-dev libmhash2 libtidy-dev libgd2-xpm-dev libgd2-xpm libmcrypt-dev libmcrypt4 mcrypt t1lib-bin libt1-dev libxml2-dev libcurl3-dev libaspell-dev libpspell-dev libxslt-dev libbz2-dev libsqlite-dev libdb4.4 db4.4-util libdb4.4++-dev libsqlite3-dev libsnmp-dev

Y luego de descomprimir php-5.2.5.tar.bz2 (descargado de la página de php.net) ejecutamos el siguiente ./configure en la raíz del código fuente:

CFLAGS=”-O3″ CXXFLAGS=”-O3 -fPIC -mtune=nocona -march=nocona \
-fPIC -pipe -fomit-frame-pointer -msse -fexceptions -ffast-math -mfpmath=sse,387″ \
./configure \
–prefix=/usr/php –sysconfdir=/etc/php5/apache2 –mandir=/usr/share/man \
–with-apxs2=/usr/sbin/apxs –libexecdir=/usr/lib/php5 –bindir=/usr/bin –libdir=/usr/lib/php5 \
–with-config-file-path=/etc/php5 –with-config-file-scan-dir=/etc/php5/conf.d \
–with-exec-dir=/usr/lib/php5/libexec –enable-inline-optimization \
–disable-debug –with-curl –with-curlwrappers –with-db4 \
–with-zlib=/usr –enable-bcmath –enable-calendar –enable-mbstring –enable-dba \
–with-libxml-dir=/usr –with-xmlrpc=shared –with-pear=/usr/share/php –with-regex \
–with-pcre-regex=/usr –enable-exif –with-t1lib –with-xsl \
–with-mhash –with-mcrypt –enable-zend-multibyte –with-pspell –enable-zip –enable-bcmath \
–with-jpeg-dir=/usr –enable-soap –enable-sockets –with-xpm-dir=/usr –with-freetype-dir=/usr/lib \
–enable-ftp –with-gd –with-png-dir=/usr –with-ttf \
–enable-json –with-mime-magic –with-tidy=/usr –with-ldap –with-xmlrpc \
–enable-gd-native-ttf –enable-gd-jis-conv –with-gettext –with-xmlrpc –with-xsl \
–with-mysql –with-mysqli \
–enable-sysvmsg –enable-sysvsem –enable-sysvshm –enable-sigchild \
–with-ldap –enable-mbstring –with-bz2 –with-iconv –with-gettext –enable-shmop \
–enable-sockets –enable-wddx –with-zlib \
–with-kerberos=/usr –with-openssl=/usr –enable-soap –with-snmp=/usr \
–with-pgsql –with-sqlite=/usr \
–enable-pdo –without-pdo-dblib –with-pdo-mysql \
–with-pdo-pgsql –with-pdo-sqlite=/usr

ejecutar:

make all

y luego:

make test

permitirá compilar y luego probar nuestro php5 en modo mpm_worker; al final un:

make install

permitirá instalar php5.2.5

Explicando brevemente las sentencias de pre-procesador:

Como verán, antes del .configure hemos ejecutado unas sentencias que mejoran el rendimiento del ejecutable php; entre ellas compilarla para el procesador que actualmente tenemos; en mi caso -mtune=nocona -march=nocona; nocona indica un procesador de doble nucleo (Core2-Duo), existen distintos modos dependiendo del ejecutable; pentium4, pentium-m, athlon-xp, prescott; siempre es bueno leer sobre -mtune y -march en el wiki de gentoo y en la guía de Compilación optimizada de Gentoo.

Apache Benchmark en un php5 con mpm_worker:

Para la ejecución de una aplicación PHP ab reporta:

ab -c 500 -n 500 http://localhost/tomates/

Requests per second: 32.66 [#/sec] (mean)

Conclusiones:

Espero que la utilización correcta de las extensiones de php; la utilización de un php thread-safe en un apache2 mpm_worker permitan a los desarrolladores crear aplicaciones más óptimas y a los servidores proveer una versión más rápida de php5 ejecutado sobre apache.

Notas finales:

La sentencia de compilación está configurada para el enviroment de debian o fedora (/etc/php5 como ruta del archivo php.ini); recuerden revisar bien el –prefix, –libdir, –bindir y otras rutas dependiendo de la distro donde deseen compilar el php5 en modo worker.

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 3.045 seguidores

%d personas les gusta esto: