Ohhnews

分类导航

$ cd ..
Baeldung原文

如何修复Java应用中MySQL的通信链路失败错误

#java#mysql#数据库连接#通信链路失败#jdbc

1. 概述

由于 MySQL 是最常见的数据库管理系统之一,我们可能需要建立 MySQL 与 Java 应用程序之间的稳定连接。这通常通过 MySQL Connector/J JDBC 驱动程序完成。然而,一个常见的问题是 Communications link failure 错误,通常以 CommunicationsException 形式抛出。虽然错误信息看似简单,但根本原因可能差异很大。

在本教程中,我们将解释如何修复使用 MySQL 的 Java 应用程序中的 Communications link failure 错误。首先,我们探讨该异常的含义及其出现时机。接着,我们介绍配置问题、网络问题、MySQL 设置以及其他潜在原因。最后,我们讨论诊断问题的不同方法以及如何规避或修复它。

值得注意的是,我们主要引用 MySQL Connector/J 8.x 与 MySQL Server 5.78.x 一起使用的情况。由于较旧的 Connector/J 5.1.x 版本与新的 8.x 版本之间存在一些差异,我们将在文中必要时明确提及。

2. 理解错误

在尝试修复问题之前,让我们了解错误出现的方式和时机。

2.1. CommunicationsException

通常,该错误发生在 Java 应用程序中,导致 JDBC CommunicationsException

$ java
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException:
Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago.
The driver has not received any packets from the server.

基本上,JDBC 驱动程序尝试与 MySQL 服务器交换数据,但没有任何数据通过

在 Connector/J 5.1.x 中,该异常通常出现在 com.mysql.jdbc.exceptions.jdbc4.CommunicationsException 包下。然而,Connector/J 8.x 将包更改为 com.mysql.cj.jdbc.exceptions.CommunicationsException。这种变化并不意外,未来版本可能继续这种趋势。

在实践中,我们经常在两种场景中遇到此错误:

  • 在初始连接尝试期间
  • 在已建立的连接上执行查询时

考虑到这一点,让我们检查常见原因。

2.2. 常见原因

虽然错误本身是通用的,但几个潜在问题在生产系统中频繁出现

  • 我们的 MySQL 服务器未运行
  • JDBC 连接 URL 包含错误的主机名或端口
  • 配置的主机的 DNS 解析失败
  • MySQL 未监听 TCP/IP 连接
  • 防火墙或代理规则阻止数据库流量
  • 服务器关闭空闲连接
  • 应用程序耗尽可用的数据库连接
  • JDBC 驱动程序版本与服务器不兼容

由于多种因素可能导致相同的异常,诊断问题可能需要多个步骤。

3. 验证基本连接

首先,确认应用程序能否访问数据库服务器。

3.1. 验证 MySQL 服务器是否运行

错误最直接的原因可能是 MySQL 服务器实例未运行。自然,如果服务未启动,JDBC 驱动程序无法访问它

这里,简单重启即可解决问题:

$ bash
# sudo service mysql restart

服务器重启后,应用程序应该能够正常重新连接。不过,该问题也可能由于间歇性重启而发生,这种情况下可能需要进一步调试。

3.2. 检查 JDBC 连接 URL

MySQL 初始连接的关键参数可能导致通信失败。

让我们分析一个典型的 MySQL JDBC 连接字符串:

$ java
jdbc:mysql://localhost:3306/mydatabase

排查问题时,我们应验证至少几个参数的正确性:

  • 主机名或 IP 地址
  • 端口号(默认:3306)
  • 数据库名称
  • 用户名和密码

特别地,错误的端口或主机名可能完全阻止应用程序连接到服务器

3.3. 测试主机名解析

与任何网络问题一样,主机名和主机地址之间的区别至关重要。名称解析失败足以阻止连接。通常有几种可能的原因:

  • 操作系统配置错误
  • 错误的 hosts 文件条目
  • DNS 服务器配置错误
  • DNS 服务器不知道主机
  • DNS 服务器无响应

这些因素中的任何一个都可能阻止 JDBC 驱动程序到达服务器。为了排查,我们可以尝试将像 localhostMYSQL-1 这样的名称替换为明确的 IP 地址,例如 127.0.0.1192.168.6.66

如果使用明确的 IP 地址可以连接,但使用主机名不行,那么 DNS 或主机名解析问题很可能是罪魁祸首。相反,如果即使使用直接 IP 地址连接仍然失败,那么根本原因更可能与网络、防火墙规则、错误端口或 MySQL 服务器配置有关。让我们逐一检查这些可能性。

4. MySQL 配置问题

如果我们能够访问托管服务器的实际硬件,但仍然遇到问题,那么 MySQL 可能未按预期配置为接受网络连接。要诊断此问题,我们通常检查 MySQL 配置文件(my.cnfmy.ini)中的选项和值。

4.1. 验证 bind-address 设置

一个常见的 MySQL 选项是 bind-address

具体来说,bind-address 的值决定了服务器监听的网络接口

$ ini
bind-address = 127.0.0.1

在这种情况下,我们只期望来自 localhost 的连接。

另一方面,我们可以接受到任何地址或接口的连接

$ ini
bind-address = 0.0.0.0

检查 bind-address 是否已正确设置为期望的值对于诊断连接问题至关重要。

4.2. 禁用 skip-networking

某些 MySQL 安装支持 skip-networking 配置选项,该选项完全禁用 TCP/IP 连接。

因此,如果配置文件中出现此设置,我们应该将其注释掉:

$ ini
# skip-networking

尽管这是一个罕见的原因,但检查此类边界情况花费时间不多,并且可能解决问题。所以,值得一试。

4.3. 调整连接超时设置

为防止资源浪费,MySQL 可能会关闭空闲时间过长的连接。

有几个参数控制此行为:

  • wait_timeout:关闭空闲非交互连接之前等待的最长时间
  • interactive_timeout:关闭空闲交互连接(例如 mysql CLI)之前等待的最长时间
  • connect_timeout:等待客户端完成初始连接握手的最长时间

如预期的那样,增加这些值可以减少意外的连接失败:

$ query
wait_timeout = 28800
interactive_timeout = 28800

在所有情况下,更改配置后,应重启 MySQL 服务。

5. 网络和环境问题

即使 MySQL 配置正确,外部因素仍可能阻止应用程序连接。

5.1. 防火墙和防病毒软件或代理限制

网络控制可能阻止应用程序与 MySQL 服务器之间的流量。例如,防火墙规则、防病毒软件或代理可能是连接问题的原因。

为了确保连接通过,我们可以使用诸如 pingtelnet 等工具进行简单检查,以及使用 nmap 进行端口测试。

无论网络设置如何,我们应确保端口 3306 在整个链路中开放且可访问。当然,如果 MySQL 配置为监听其他端口,则应该验证针对该特定端口(而不是 3306)的连接。

5.2. IPv6 与 IPv4 问题

某些环境将名称解析为 IPv6 地址,例如 localhost::1 而不是其 IPv4 等效地址 127.0.0.1。然而,如果 Java 应用程序环境和 MySQL 服务器使用不同的 IP 版本,我们可能会遇到连接失败

常见解决方案:

  1. 使用 127.0.0.1 而不是 localhost
  2. 使用 JVM 选项 -Djava.net.preferIPv4Stack=true 启动 Java

这将强制应用程序优先使用 IPv4 网络。

6. 应用层原因

应用程序的实现方式也会影响连接的建立和稳定性。

6.1. 连接池中的过期连接

带有过期条目的连接池是导致 MySQL 连接问题的常见原因。 当应用程序尝试在服务器已经关闭的连接上执行查询时,JDBC 驱动程序会报告通信失败。

通常,解决方案是切换到现代连接池:

  • HikariCP
  • Apache DBCP
  • C3P0

原因是这些实现在将连接返回给应用程序之前会验证连接。

在实践中,连接池可能需要显式配置以验证或回收过期连接。 例如,HikariCP 使用几个设置:

  • maxLifetime
  • idleTimeout
  • keepaliveTime

Apache DBCP 和 C3P0 支持验证查询和连接测试选项。具体细节不在本文讨论范围之内。

6.2. 连接池耗尽

连接限制可能需要在尝试新连接之前关闭旧连接。 否则,MySQL 服务器可能拒绝连接尝试。

特别地,确保正确关闭 JDBC 资源至关重要:

$ java
try (Connection conn = dataSource.getConnection()) {
    // execute queries
}

连接池也可以限制和回收连接。

即使应用程序关闭了所有 JDBC 资源,数据库服务器仍然可能拒绝新连接。例如,MySQL 可能达到 max_connections 限制或遇到临时过载情况。在这种情况下,应用程序可能会收到不同的异常,包括诸如 Too many connections 之类的错误,而不是通用的 CommunicationsException

此外,配置的池大小也很重要。如果连接池允许的并发连接数超过 MySQL 接受的连接数,则服务器连接可能会耗尽。因此,池连接大小和 MySQL 中的 max_connections 之间应该保持适当的平衡。

6.3. 更新 JDBC 驱动程序

较旧版本的 MySQL Connector/J 驱动程序可能无法与较新的数据库版本可靠地协同工作。

更新依赖通常可以解决兼容性问题:

$ xml
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.33</version>
</dependency>

对于 Connector/J 8.x,Maven 构件也可能出现在更新的坐标 com.mysql:mysql-connector-j 下,具体取决于项目设置和存储库元数据。

与计算领域的其他方面一样,保持驱动程序最新可确保与 MySQL 协议的兼容性并提高连接稳定性。

7. 总结

在本文中,我们探讨了如何诊断和修复使用 MySQL 的 Java 应用程序中的 Communications link failure 错误。

当 JDBC 驱动程序无法与数据库服务器通信时,会发生此错误。常见原因包括服务器停机、连接配置错误、MySQL 网络设置、防火墙限制、IPv6 解析问题、连接池过期连接以及应用程序内部的连接耗尽。

仔细检查连接、审查 MySQL 配置、使用可靠的连接池、更新 JDBC 驱动程序以及检查诊断日志可以显著减少依赖 MySQL 的 Java 应用程序中的通信失败。