Resolver el CommunicationsException con JAVA y MYSQL
Hay varias razones por las cuales puede aparecer esta excepción y no hay ninguna manera para solucionar todos los casos. Quizá la tecnología que usas sea distinta a la mía pero aquí puedes encontrar pistas que te ayuden a encontrar una solución.
* Yo normalmente uso MariaDb 10.10, Java JDK 1.6 y Linux Ubuntu 12 LTS y un pool de conexión con Tomcat.
Error: La API deja claro que hay un problema que comunicación entre el driver y la base datos.
Puede aparecen en los siguientes casos.
- No puedes conectarte a la base de datos en local #scenario1 .
- No puedes conectarte a la base de datos cuando está en otra IP #scenario2 .
- Puedes conectarte a la base de datos pero a menudo salta esta excepción #scenario3.
- Puedes conectarte a la base de datos pero salta esta excepción cuando usas un pool de conexión #scenario4.
* Cambiar de escenario puede ayudarte a saber cual es la causa del problema.
No puedes conectarte a la base de datos en local
La base de datos está instalada en la máquina local pero no puedes conectar.
- La base de datos esta caída.
- Tu URL de conexión o el puerto especificado es incorrecto.
- Hay algo bloqueando el tráfico de red entre la base de datos y el código (ex. a firewall).
- La base de datos no está bien configurada y está rechazando las conexiones.
Pasos:
- Usa cualquier monitor para comprobar que la base de datos funciona. ej. top, grep, taskmanager, taskmonitor, etc. o intenta acceder a la consola. ej. mysql -u root -pmypassword
- Comprueba que tu base de datos está escuchando por el puerto y esta preparada para recibir nuevas conexiones. #checkListen.
- Haz un telnet a tu base de datos #telnet.
- Usa el ejemplo de código #testsnippet para verificar que el problema no está en tu código.
- Comprueba los archivos de configuración que están en tu instalación de MySQL o MariaDB #configfiles.
No puedes conectarte a la base de datos cuando está en otra IP
La base de datos a la que intentas conectarte está en otra máquina distinta desde donde intentas conectar.
- Un firewall impide que te conectes a la base de datos
- El puerto no está abierto o el router no está redirigiendo al puerto correctamente.
- MySQL no está aceptando conexiones remotas.
Pasos:
- Comprueba desde dentro. En la misma máquina donde está instalada la base de datos. Comprueba que esta escuchando y preparada para recibir conexiones #checkListen.
- Comprueba desde fuera. Haz un telnet a la base de datos #telnet desde tu máquina.
- Comprueba tus configuraciones de enrutamiento. Routers, reglas de redireccionamiento, puertos abiertos, etc.
- Comprueba firewalls y abre puertos si es necesario.
- Comprueba los archivos de configuración que están en tu instalación de MySQL o MariaDB #configfiles.
Puedes conectarte a la base de datos pero a menudo salta esta excepción
- El timeout de la base de datos cierra tus conexiones.
- Hay algo en tu código que causa que la base de datos se caiga o no procese las conexiones correctamente (ej. demasiadas conexiones abiertas, no tiene suficiente memoria, etc.)
- Hay algún elemento externo que está apagando la base de datos.
- Hay un firewall que está detectando la conexión y cerrandola. Puede ser por exceso de tiempo de inactividad.
Pasos:
- Usa cualquier monitor para comprobar que la base de datos funciona. ej. top, grep, taskmanager, taskmonitor, etc. o intenta acceder a la consola. ej. mysql -u root -pmypassword
- Comprueba que tu base de datos está escuchando y esta preparada para recibir nuevas conexiones. #checkListen.
- Haz un telnet a la base de datos #telnet.
- Comprueba el timeout #timeout
- Usa el ejemplo de código #testsnippet para averiguar con que frecuencia está ocurriendo. Es conveniente que sea él único código en ejecución mientras buscas la causa a la excepción.
- Comprueba la cantidad de conexiones activas que hay #showactive y que no estás excediendo la cantidad máxima #configfiles
- Comprueba los logs de error de MySQL ej. in linux /var/logs
Puedes conectarte a la base de datos pero salta esta excepción cuando usas un pool de conexión
- Tu URL de conexión o el puerto especificado es incorrecto.
- Los parámetros establecidos en el pool no coinciden con los de MySQL, ej. has especificado un timeout mayor al que esta establecido en la base de datos, o has excedido la mayor cantidad de conexiones posible. No todos los pooles de conexión impiden que ocurra un timeout en la base de datos o no tienen la opción para especificar el timeout de la base de datos y el timeout interno del pool.
- El pool tiene errores de código.
Pasos:
- Usa el ejemplo de código #testsnippet para confirmar que es son las conexiones que van a traves del pool las que no funcionan correctamente.
- Comprueba el timeout #timeout
- Comprueba la API de tu pool de conexión.
- Borra el ?autoReconnect=true en la URL de conexión si está allí.
- Usa una query de validación si tu pool de conexión tiene esa opción. ex. validationQuery="SELECT 1"
- Busca en tu código por cualquier Connection, Statement and ResultSet que no se hayan cerrado.
Ejemplos de Tomcat:
- Haz clic para ver ejemplos verificados de pool sobre #tomcat
Comprueba si la base de datos está escuchando
- Linux cambia el puerto por el tuyo si lo necesitas: sudo netstat -anltp|grep :3306 or sudo nmap -p 3306 localhost
- Windows el puerto de tu base de datos debería estar entre las listadas con este comando: netstat -an
* El puerto debería estar escuchando, si no lo está puede que haya un firewall o que la instalación de la base de datos este mal.
Test Code Snippet
import java.sql.*; public class Connect { private static final long PAUSE_MILLIS = 70000; public static void main(String[] args) { Connection conn = null; try { String userName = "root"; String password = "mypassword";//cambia por el tuyo String url = "jdbc:mysql://localhost:3306";//cambia por el tuyo Class.forName("com.mysql.jdbc.Driver").newInstance(); conn = DriverManager.getConnection(url, userName, password); PreparedStatement ps1; System.out.println("Database connection established"); while(true){ try { conn.setAutoCommit(true); ps1 = conn .prepareStatement("SELECT CURRENT_TIMESTAMP() as Date"); ResultSet rs = ps1.executeQuery(); while (rs.next()) { System.out.println(rs.getString("Date")); } rs.close(); ps1.close(); } catch (SQLException e) { e.printStackTrace(); } System.out.println("Espera al siguiente ciclo..."); Thread.sleep(PAUSE_MILLIS); } } catch (Exception e) { System.err.println("No puedo conectar"); System.err.println(e.getMessage()); e.printStackTrace(); } finally { if (conn != null) { try { conn.close(); System.out.println("Se termina la conexión"); } catch (Exception e) { } } } } }
* Cambia el PUERTO, PASSWORD, PAUSE_MILLIS según lo vayas necesitando para encontrar la causa de la excepción.
Telnet a MySQL
Usar el telnet puede ser muy útil para averiguar si puedes llegar a la base de datos ej. en consola escribe telnet youdatabaseip port. o telnet youdatabaseip:port Si estás usando windows y si el telnet no está instalado busca. search install telnet
Si ves este mensaje es que estás llegando correctamente. Pulsa Intro 2 veces para salir.
Prueba diferentes combinaciones como por ejemplo:
- telnet localhost 3306
- telnet 127.0.0.1 3306
- telnet 192.168.1.10 3306
- telnet 82.16.23.22 3306
* Prueba diferentes combinaciones, si alguna falla puede darte una pista de que el problema este relacionado con la conectividad en la red.
MySql Archivos de configuración
- Linux MySQL ej: /etc/my.cnf
- Windows ej: MySQL: C:\Program Files\mysql\bin\my.ini
- Comenta o cambia "bind-address" y cambia la IP a: bind-address="127.0.0.1" o bind-address="0.0.0.0"
- Comenta "skip-networking"
* Si el problema es ocasional quizá debas establecer las siguientes propiedades.
- wait_timeout = number
- interactive_timeout = number
- connect_timeout = number
MySQL TimeOut
- Entra en consola MySQL ej. mysql -u root -pmypassword
- Desde la consola muestra todas las variables relacionadas con el tiempo show global variables like '%time%'
* El wait_timeout esta en segundos y cerrará todas las conexiones abiertas. Puedes actualizar tus archivos de configuración #configfiles or restablecerlo desde consola ej. durante 10 minutos set global wait_timeout=600;
Mostrar conexiones activas
- En la Consla MySQL show full processlist; o show full processlist\G; o SELECT host,count(host) FROM information_schema.processlist GROUP BY host;
* Muestra la cantidad de conexiones activas en la base de datos.
- Info muestra el comando de MySQL en ejecución, NULL indica que esta abierta la conexión pero que no hay ningun comando ejecutándose.
- Usa el comando kill id_process para borrar procesos.
Ejemplos de POOL con Tomcat
Pool con query de validación, máximo 2 conexiones en IDLE (latentes)
<Resource name="jdbc/mysql" auth="Container" type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/blog" username="root" password="apple" maxActive="10" maxIdle="2" validationQuery="SELECT 1"/>
Pool que no tiene conexiónes en IDLE y que las cierra trás 45 segundos de duración.
<Resource name="jdbc/mysql" auth="Container" type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/blog" username="root" password="apple" maxActive="10" maxIdle="2" maxWait="45" maxAge="45" removeAbandoned="true" removeAbandonedTimeout="45"/>
JSP Para pruebas. (No se debería usar como ejemplo puesto que una JSP no es el lugar para acceder a base de datos)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.sql.Connection" %> <%@ page import="java.sql.PreparedStatement" %> <%@ page import="java.sql.ResultSet" %> <%@ page import="java.sql.SQLException" %> <%@ page import="javax.naming.Context" %> <%@ page import="javax.naming.InitialContext" %> <%@ page import="javax.naming.NamingException" %> <%@ page import="javax.sql.DataSource" %> <% Context context = new InitialContext(); DataSource ds = (DataSource) context.lookup("java:comp/env/jdbc/mysql"); if(ds!=null){ Connection con = ds.getConnection(); con.setAutoCommit(true); PreparedStatement ps1 = con .prepareStatement("SELECT CURRENT_TIMESTAMP() as Date"); ResultSet rs = ps1.executeQuery(); while (rs.next()) { out.println("Date From DataBase " +rs.getString("Date")); } rs.close(); ps1.close(); con.close(); } %>