El pool de conexiones es una técnica usada en aplicaciones Web para mejorar el rendimiento de las aplicaciones Web.
Antes de ver en que consiste , veamos como funciona la creación de conexiones en una aplicación clásica de escritorio cliente-servidor y luego veamos los problemas se seguir con dicha estructura en una aplicación web.
En una aplicación de escritorio se crea una conexión de base de datos al iniciar la aplicación y se cierra al finalizar la aplicación. Es decir que cada usuario que inicia la aplicación tiene una conexión en exclusiva para él. Y obviamente sería imposible compartirlas ya que cada aplicación estará en un ordenador independiente.
En una aplicación Web podríamos seguir un esquema similar , en el que cada usuario nuevo que se conecta a nuestra aplicación se le crea una conexión y al salir de la aplicación que se cierre su conexión.
Esto que aparentemente es sencillo tiene unos problemas debido a la diferente naturaleza de las aplicaciones de escritorio y las web.
Característica | Escritorio | Web | Problemas |
---|---|---|---|
Número de usuarios | Bajo. Suele estar limitado por el tamaño de una oficina o edificio. | Muy alto. Potencialmente toda internet. | En la Web si cada usuario tiene una conexión propia para él se podría saturar el servidor de base de datos |
Momento de cierre de la conexión | Claramente definido al cerrar la aplicación | Vagamente definido | En Web es complejo saber cuando el usuario abandona el portal (que no la página) para en ese momento cerrar la conexión |
Si siguiéramos el mismo patrón de creación de conexiones de aplicaciones de escritorio en aplicaciones web acabaríamos con una cantidad enorme de conexiones activas (debido al gran número de usuarios) y con gran cantidad de conexiones abiertas sin usar (debido a usuarios que abandonan el portal y no lo hemos detectado)
Consecuencia de los anterior al tener tantas conexiones a la base de datos se acabaría cayendo el servidor de base de datos debido a los recursos consumidos por todas las conexiones.
Podemos pensar que nuestro servidor puede aguantar todas esas conexiones ya que podemos tener pocos usuarios pero no suele ser así debido a:
Una solución chapuza que nos evitaría las conexiones sin usar sería que se creara la conexión al iniciar cada petición web y se cerrara al finalizar dicha petición web. ¿El problema de eso? Crear y cerrar una conexión es muy costoso. Lo que tendríamos es una aplicación lentísima.
La solución del pool de conexiones tiene que solucionar los siguientes problemas:
La solución de cerrar la conexión se solucionaría fácilmente con la técnica de crearla al inicio de la petición y de cerrarla al final de la petición. Pero como ya hemos visto tiene el problema de lo lenta que sería la aplicación. Pero eso se puede solucionar con el Pool de conexiones
Así que por fin pasemos a explicar en que consiste el pool de conexiones:
El pool de conexiones consiste en tener un conjunto (pool) de conexiones ya conectadas a la base de datos que puedan ser reutilizadas entre distintas peticiones.
Veamos como funciona:
¿Que hemos conseguido con el pool?
Veamos ahora un ejemplo de uso de un pool de conexiones que ha sido configurado de la siguiente forma:
En la siguiente gráfica vemos un ejemplo de como podrían evolucionan las conexiones del pool:
Tiempo (s) | Maximo | Activas | Esperando |
---|---|---|---|
10 | 200 | 100 | 50 |
20 | 200 | 120 | 30 |
30 | 200 | 140 | 10 |
40 | 200 | 140 | 50 |
50 | 200 | 140 | 50 |
60 | 200 | 100 | 90 |
70 | 200 | 100 | 50 |
80 | 200 | 70 | 80 |
90 | 200 | 70 | 50 |
100 | 200 | 150 | 0 |
110 | 200 | 150 | 50 |
120 | 200 | 90 | 110 |
130 | 200 | 90 | 50 |
En la gráfica podemos ver como el máximo de conexiones que puede haber es 200 y por lo tanto de usuarios activos es de 200. Este valor es constante ya que es tal y como hemos configurado el pool. Eso no quiere decir que no pueda haber mas usuario reales en ese momento siempre y cuando estén mirando su navegador y no actuando con el servidor. También se ve como a lo largo del tiempo va variando el nº de usuarios activos, sin llegar nunca a 200.
En el instante 60 bajan bruscamente el nº de conexiones activas de 140 a 100 por lo que se quedan 90 conexiones esperando, como son demasiadas el propio pool desconecta conexiones y en el instante 70 vuelven a ser solo 50 las conexiones esperando a ser usadas. Lo mismo pasa en el instante 80.
En el instante 100 hay una fuerte subida de conexiones activas y no hay suficientes esperando para absorber la subida de usuarios, asi que en ese caso se deben crear 30 nuevas conexiones. Esto hará que se deban esperar un poco los 30 usuarios que les toque la creación de la conexión.Sin embargo en el instante 120 hay una caída brusca de usuarios con lo que vuelven a sobrar conexiones esperando. En ese caso se da la curiosidad de que hay mas conexiones esperando (110) que activas (90). En el siguiente instante (110 s) vemos como el pool ha cerrado conexiones para volver a tener las 50 con las que ha sido configurado.
Desde Java podemos acceder al pool de conexiones usando la interfaz javax.sql.DataSource. Al llamar al método getConnection() se obtendrá una conexión del pool de conexiones y no se creará nueva en ese momento. Cuando llamemos al método java.sql.Connection.close() realmente la conexión no se cerrará sino que se devolverá al pool de conexiones.
DataSource dataSource; Connection connection=dataSource.getConnection(); //Obtener la conexión del pool //Usar la conexión connection.close(); //No se cierra realmente la conexión sino que se retorna al pool
Ahora nos queda saber como obtener un objeto DataSource. Para obtener un objeto DataSource necesitamos primero configurar el pool de conexiones mediante el fichero context.xml
, cosa que vamos a ver en el siguiente apartado. Ahora simplemente debemos saber que en un mismo servidor web puede haber muchos pool de conexiones por lo que cada uno de ellos tiene un nombre único. Para obtener el DataSource asociado a un pool de conexiones debemos incluir el siguiente código:
InitialContext initCtx=new InitialContext();; Context envCtx = (Context) initCtx.lookup("java:comp/env"); DataSource dataSource = (DataSource)envCtx.lookup("jdbc/hibernate1");
Siendo jdbc/hibernate1
el nombre del pool de conexiones.
Por último nos queda configurar el pool de conexiones. Cada servidor web puede usar su propia implementación del pool de conexiones y se configura de forma específica. Nosotros vamos a explicar como configurar el pool que viene incluido en Tomcat 7.
El pool se configura añadiendo un fichero llamado context.xml
en la carpeta META-INF
.
<?xml version="1.0" encoding="UTF-8"?> <Context path="/ejemplo05"> <Resource type="javax.sql.DataSource" auth="Container" name="jdbc/hibernate1" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/hibernate1" username="hibernate1" password="hibernate1" maxActive="100" maxIdle="30" maxWait="10000" validationQuery="SELECT 1 FROM dual" /> </Context>
envCtx.lookup(name)
Hasta ahora solo hemos indicado los atributo básicos de conexión a la base de datos, veamos ahora los atributos concretos del pool:
maxIdle
. Ésto se hace para ahorrar recursos del servidor de base de datos.SELECT
que lanza Tomcat para comprobar que la conexión aun está conectada a la base de datos y que ésta ultima no la ha cerrado unilateralmente.validationQuery
ya que sino se podría usar una conexión que la base de datos ha cerrado pero que Tomcat cree que aun está conectada.
maxActive
ha sido renombrado a maxTotalmaxWait
ha sido renombrado a maxWaitMillisMas información en Apache Tomcat - Migration Guide - Tomcat 8.0.x - Database Connection Pooling y en Apache Commons BDCP