Pool de conexiones en PHP

Roman Jesus roman_astorga en hotmail.com
Vie Feb 10 16:17:36 CLST 2006


Amigos de la lista:
 
Al iniciar mi comentario pensaba tener razones mas que justificadas para mi
forma de trabajo, pero ahora que lo pienso con mas detenimiento no estoy tan
seguro.
 
Bien les expongo de que se trata.
 
Uno de los mayores peligros que enfrentamos cuando programamos en una Web es
el tema de la conexión a una Vd., sobre todo cuando la información que
estamos desplegando es lo suficiente grande para que se produzca un pequeño
retardo en la consulta y el retorno de la información.
 
No falta quien  por desgracia presionan la tecla F5 reiteradas veces o la
dejan presionada, en otras palabras se interrumpa la carga de la pagina con
el propósito de generar una sobre carga de conexiones simultaneas.
 
Lo cual podrá permitir que se generen N -1 cantidad de conexiones
simultaneas desde el mismo punto y si sumamos al menos dos tipos así
haciendo esto al mismo tiempo, nuestro amado servidor no quedaría
desprotegido...
 
 
 
 


  _____  

De: php-bounces en listas.inf.utfsm.cl [mailto:php-bounces en listas.inf.utfsm.cl]
En nombre de Rodrigo Fuentealba
Enviado el: 10-02-2006 15:09
Para: Lista de desarrolladores en PHP
Asunto: Re: Pool de conexiones en PHP


JuanPa wrote: 

Hola listeros....
Te agradezco nuevamente Rodrigo por dar tu valioso tiempo aclarando mis
dudas, 

A raiz de es comentario es que he optado por utilizar mis clasicas
conexiones no persistentes .


ejalé ;)



Es mejor usar conexiones no persistentes, es decir, abriendo la conexión
al principio de tu script y cerrando la conexión al final, pues siempre 
vas a tener páginas que no hagan ninguna consulta a la base de datos...
¿Para qué, entonces, tendrás una conexión abierta? Es únicamente un
desperdicio de recursos.


Ademas no voy a esperar que el GC (Garbage Colector) haga su trabajo, una
ultima cosa... 


Bueno el recolector de basura funcionará igual, pero cerrando menos cosas.


Por eso es mejor abrir la conexión al principio de un script y cerrarla
al final, y si tienes funciones que recuperan datos como el caso de tu 
buscar alumno, en vez de abrir una conexión nueva, preguntes si ya hay
una conexión abierta para aprovecharla.


Pero ¿ esto no significa que la conexion deberìa ser persistente? o te
refieres a que deberia pasar por parametros a mi objeto conexion que tiene
un recurso de conexion interno($link) y luego verificar si esta está
abierta. 


Una conexión no persistente se abre al inicio de un archivo (supongamos
insertar.php) y se debe cerrar al final de éste. Si tienes un archivo que se
llama modificar.php, ese debe declarar otra conexión, pues la primera no
estará.

La conexión persistente se abre al inicio de la sesión (mientras el usuario
está conectado) y puede pasarse por varias páginas. Si tienes un archivo
insertar, modificar, eliminar, buscar, etc, cuantoquieras.php, todos esos
archivos usarán conexiones persistentes.

<!-- leo de otro mail
una conexion persistente permite re-utilizar una conexion ya abierta, ademas
de tener que cerrarla explicitamente sino queda "abierta"! la idea es evitar
el tiempo (milisegundos?) que se ocupa en "establecer" una conexion. su uso
a mi entender esta reservado a sitios con muchas conexiones nuevas, con
procesos cortos. 
-- leido -->

Esto es cierto, yo dije "una vez por usuario/sesión", queriendo enfocar a
que la conexión estaba abierta y podía ser aprovechada por varias páginas
que visita un usuario desde un navegador. (pues una conexión se abre
solamente para un usuario).



Y finalmente cuando dices... 


Cualquiera de las dos conexiones funciona así, la diferencia es que la 
persistente la abres una vez por usuario (o por conexion?) ...


Esto significa ¿ Que la conexion seguira abierta en cada script que valla
ejecutando el usuario, mientras no se cierre explicitamente en otro script o
cuando el usuario cierre el navegador ? 


Ahhh, eso con ejemplo (ahora haré código real con adodb)... "notese,
solamente una conexión no persistente"

<?php
    echo("Abriendo la primera conexión")
    $conexion = ADONewConnection("postgres");
    $conexion->NConnect($host,$user,$pass,$base);
    $sql = $conexion->Prepare("SELECT * FROM alumno WHERE rut_alumno = ?");
    $rs = $conexion->Execute($sql,array('12345678-9'));
    $conexion->Close();

    echo("Abriendo la segunda conexión");
    $conexion = ADONewConnection("postgres");
    $conexion->NConnect($host,$user,$pass,$base);
    $sql = $conexion->Prepare("SELECT * FROM apoderado WHERE rut_alumno =
?");
    $rs = $conexion->Execute($sql,array('12345678-9'));
    $conexion->Close();

    $conexion = ADONewConnection("postgres");
    $conexion->NConnect($host,$user,$pass,$base);
    $sql = $conexion->Prepare("SELECT * FROM cuotas WHERE rut_alumno = ?");
    $rs = $conexion->Execute($sql,array('12345678-9'));
    $conexion->Close();
?>

El ejemplo anterior es un script demasiado mega mal hecho. Abres tres veces
una conexión nueva y no destruyes las conexiones anteriores. Digamos que
solamente las desconectas. Uhhh, en este pequeño script te gastaste tres
conexiones. ¿Qué mal, no?. Pues ahora veamos cómo lo habría hecho con una
sola conexión no persistente.

<?php
    echo("Abriendo la conexión")
    $conexion = ADONewConnection("postgres");
    $conexion->NConnect($host,$user,$pass,$base);

    dibujemos();

    // con la misma conexion no persistente, puedes hacer UNA
    $sql = $conexion->Prepare("SELECT * FROM alumno WHERE rut_alumno = ?");
    $rs = $conexion->Execute($sql,array('12345678-9'));

    juguemos_en_el_bosque();

    // DOS
    $sql = $conexion->Prepare("SELECT * FROM apoderado WHERE rut_alumno =
?");
    $rs = $conexion->Execute($sql,array('12345678-9'));

    mientras_el_lobo_no_esta();

    // TRES y más consultas en el mismo script.
    $sql = $conexion->Prepare("SELECT * FROM cuotas WHERE rut_alumno = ?");
    $rs = $conexion->Execute($sql,array('12345678-9'));

    lobo_esta(?);

    // Y antes de cerrar el script, la conexión se cierra sola.
    $conexion->Close();

    terminado();

    exit();
?>

El ejemplito este aprovecha una sola conexión no persistente, pero usando
solamente "UN" script, lo cual a pesar de que está bien, te ensucia un poco
el código y te lo hace un poquito ilegible, por todas esas SQL por aquí y
por allá. OK, ahora vamos a hacer un script para las funciones, un script
para abrir y cerrar la conexión, y tu script de lógica de negocios:

<!-- conexion.php -->
<?php
    // nuestra función que nos abre muchas puertas
    function abrir_conexion()
    {
        require_once('adodb/adodb.inc.php');

        $host = "123.123.123.123";
        $user = "postgres";
        $pass = "nula";
        $base = "carepescao"; 

        $conexion = ADONewConnection("postgres");
        $conexion->NConnect($host, $user, $pass, $base);

        if(!$conexion->IsConnected)
        {
           die("Arghhh mi jefe me va a matar! me pitié la conexión");
        }
    }

    // la función que cierra con llave cuando nos vayamos ;)
    function cerrar_conexion();
    {
        if($conexion->IsConnected)
        {
            $conexion->Close();
        }       
    }

?>

<!-- leo nuevamente:
originalmente tu preguntaste por un "pool" de conexiones lo cual es
diferente ya que ahi se puede compartir conexiones *entre* procesos, y eso
en PHP se tiene que hacer con ayuda externa.
-->

Nótese que compartir conexiones entre procesos (pooling) tiene diferencias
con aprovechar las conexiones no persistentes: lo que vamos a hacer en el
siguiente archivo aprovecha una conexión no persistente que esté abierta, en
vez de estar abriendo y cerrando. Esto para mí también es llamado pooling,
pero básico, y aunque no sea un proceso automatizado, así es como trabaja...
La idea es siempre tener una y sólo una conexión disponible cuando la usemos
sin tener que abrirla más que lo necesario, y cerrarla cuando ya tengamos
todas nuestras operaciones hechas y no vayamos a usarla de inmediato, bien
por estar requiriendo datos, bien por ya haberlos obtenido y estamos
mostrándolos en otra página. La cerramos para liberar recursos y así
optimizamos cualquier cantidad de memoria. Esta clase de pooling no comparte
conexiones entre procesos (para mí un proceso tiene un PID y tiene un
destino distinto).

<!-- sql.php -->
<?php
    require_once 'conexion.php';

    // una funcion que lo unico que hace es retornar el recordset.
    function buscar_alumno($rut)
    {
        if(!$conexion->IsConnected || !isset($conexion))
        {
           abrir_conexion();
       }
       $sql = $conexion->Prepare("SELECT * FROM alumno WHERE rut_alumno =
?");
       $rs = $conexion->Execute($sql,array($rut));
       return $rs;
    }

    // una funcion que lo unico que hace es retornar el recordset.
    function buscar_apoderado($rut)
    {
        if(!$conexion->IsConnected || !isset($conexion))
        {
           abrir_conexion();
       }
       $sql = $conexion->Prepare("SELECT * FROM apoderado WHERE rut_alumno =
?");
       $rs = $conexion->Execute($sql,array($rut));
       return $rs;
    }

    // una funcion que lo unico que hace es retornar el recordset.
    function buscar_cuotas($rut)
    {
        si la conexión no existe, o no se ha seteado, entonces abrimos la
conexion. Si la conexion existe, para que?
        if(!$conexion->IsConnected || !isset($conexion))
        {
           abrir_conexion();
        }
        $sql = $conexion->Prepare("SELECT * FROM cuotas WHERE rut_alumno =
?");
        $rs = $conexion->Execute($sql,array($rut));
        return $rs;
    }
?>

<!-- bussiness.php -->
<?php
    require_once 'conexion.php';
    require_once 'sql.php';

    // abrimos la conexion();
    abrir_conexion();

    // procesamos algunas cosas...
    dibujemos();

    // llamamos a nuestra consulta.
    // esto ya es lógica de negocios, ¿no?   
    $resultado1 = buscar_alumno($rut);

    // tururururu, me gusta cantar...
    juguemos_en_el_bosque();

    // nuestra logica de negocios requiere que
    // busquemos al responsable por el cabro de ... ;)
    $resultado2 = buscar_apoderado($rut);

    // el lobo andaba comiéndose unas abuelitas por ahi
    mientras_el_lobo_no_esta();

    // de nuevo, no necesitamos de SQL feos por ahi...
    $resultado3 = buscar_cuotas($rut);

    // mostramos los resultados en la pantalla...
    lobo_esta(?);

    // cerramos la conexion...
    cerrar_conexion();

    // cerramos algunas otras cosillas, o bien cargamos el footer, que se
yo,
    terminado();

    // una vieja costumbre mía, heredada de los antiguos BASIC de ATARI.
    // nadie me ha convencido de que esto no es necesario
    exit();
?>

Como puedes ver, finalmente son más archivos, pero el que te interesa que es
la lógica de negocios, te queda lindo para leer. La cosa es diseñar un poco
más ordenado. El que programes en 3, 4, 5 o 100 capas si quieres no
significa que las capas no se interconecten, pues la lógica de presentación
necesita que la lógica de negocios le pase datos, y la lógica de negocios
necesita conectarse a la base de datos, para que tenga algo que procesar!!!


Mil disculpas si soy muy pregunton, pero siempre me gusta sacar conclusiones
valiosas de las conversaciones.... Gracias de antemano...


Cero dramas. Si Google no es claro en el asunto (de hecho no lo fue cuando
lo investigué) no hay problemas en discutirlo y aclarar las dudas.


Nos leemos
-----------------
Juan Pablo

------------ próxima parte ------------
Se ha borrado un adjunto en formato HTML...
URL: http://listas.inf.utfsm.cl/pipermail/php/attachments/20060210/bb13ef6c/attachment-0001.html


Más información sobre la lista de distribución PHP