Article updated on

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

img/0/27/_002.jpeg

* 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.

img/0/27/_001.jpeg

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%' 

img/0/27/_003.jpeg

* 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;

img/0/27/_005.jpeg

* 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();
}
%>