什么是java.io.EOFException,错误信息:无法从服务器读取响应。预期读取4个字节,但实际读取了0个字节。

54

这个问题在 Stack Overflow 上被问了几次,在其他网站上也被问了很多次。但是我没有得到满意的答案。

我的问题:
我有一个使用简单的 JDBC 连接通过 Glassfish 应用服务器连接到 mysql 数据库的 Java web 应用程序。

我在 Glassfish 服务器中使用了连接池,并进行了以下配置:
初始池大小:25
最大池大小:100
池调整数量:2
空闲超时:300 秒
最大等待时间:60,000 毫秒

该应用已经部署了三个月,一直运行良好。
但是在过去的两天中,在登录时出现了以下错误。

部分堆栈跟踪信息

com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException: No operations allowed after connection closed.Connection was implicitly closed due to underlying exception/error:  

** BEGIN NESTED EXCEPTION **  

com.mysql.jdbc.CommunicationsException  
MESSAGE: Communications link failure due to underlying exception:  

** BEGIN NESTED EXCEPTION **  

java.io.EOFException  
MESSAGE: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.  

STACKTRACE:  

java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.  
at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1997)  
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2411)  
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2916)  
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1631)  
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1723)  
at com.mysql.jdbc.Connection.execSQL(Connection.java:3256)  
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1313)  
at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1448)  
............  
............  
my application traces....  
为什么突然出现了这个错误?我为此浪费了很多时间。

编辑:即使在重新启动服务器后,问题仍然存在。根据DBA的说法,两个重要的mysql服务器配置是:
wait_timeout:1800秒
connect_timeout:10秒
注意:在同一台服务器上部署的其他应用程序连接到相同的数据库并使用不同的池,在运行顺畅。

编辑-2:阅读了很多内容并期望有些积极的结果后,我对我的连接池进行了以下更改。

最大等待时间:0(之前为60秒)
连接验证:必需的
验证方法:
表名称:Demo
最多验证一次:40秒
重试创建连接尝试:1
重试间隔:5秒钟
最大连接使用量:5

这样做后,应用程序连续运行了3天。但我从中得出了一个非常奇怪和有趣的结果。在监视连接池时,我发现了这些数字:

NumConnAcquired:44919计数
NumConnReleased:44919计数
NumConnCreated:9748计数
NumConnDestroyed:9793计数
NumConnFailedValidation:70计数
NumConnFree:161计数
NumConnUsed:-136计数

如何才能使NumConnFree变为161,而我的最大池大小=100
如何才能使NumConnUsed变为负数-136
如何NumConnDestroyed的数量大于 NumConnCreated 的数量


1
尝试在应用程序和数据库中使用相同或更小的超时值。例如,如果您的应用程序的空闲超时时间比数据库的超时时间长,那么您的应用程序将尝试重用已被数据库服务器关闭的连接。 - Adrian
它是否有助于增加池调整大小的数量?在此处查看原因:http://docs.oracle.com/cd/E19316-01/820-4343/abehq/index.html - perissf
11个回答

14

连接失败,可能是由于防火墙闲置超时等原因。如果您的JDBC驱动程序未配置为在连接失败时重新连接,则此错误将不会消失,除非您打开一个新连接。

如果您正在使用数据库连接池(您肯定在使用,对吧?),那么您可能希望启用其连接检查功能,例如发出查询以检查连接是否正常工作,然后再将其返回给应用程序。在Apache commons-dbcp中,这称为validationQuery,通常设置为一些简单的内容,如SELECT 1

由于您正在使用MySQL,应该使用特定于Connector/J的“ping”查询,该查询比实际发出真正的SQL查询更轻量级,并将验证查询设置为/* ping */ SELECT 1(需要确切地按照ping部分)。


1
“需要精确匹配”的链接已经损坏。有没有想法原始页面说了什么? - Joeblade
1
@mrjink,我又修复了链接。以后参考(当Oracle再次移动时),参考的是MySQL Connector/J版本5.1.3发布说明,其中最后一项包含要使用的语法。 - Christopher Schultz

9

这是Java中的EndOfFileException,当您的光标起始点在终点上或因某些意外异常而关闭数据库连接时会发生。


6
这是EOFException的一般解释,但并不能真正帮助Mukund所询问的JDBC问题。 - Idris Mokhtarzada

9
很可能意味着数据库已重新启动或与数据库的网络连接已断开(例如,NAT连接已超时)...而您的Web应用程序正在尝试使用过期的数据库连接。如果在重启Web容器后问题仍然存在,则可能是更严重的问题。
How can the NumConnFree become 161 as I have Maximum Pool Size = 100 ?
How can the NumConnUsed become -136, a negative number ?
How can the NumConnDestroyed > NumConnCreated ? 

表面上看,这些没有意义。然而,它们可能只是由于某些使用计数器以非线程安全的方式更新导致的。这不一定与您最初的问题有关。

我认为可能存在一些配置问题,请查看我的编辑。 - mukund

8

虽然我没有确定性的解决方案,但似乎有些东西在干扰应用服务器和数据库之间的通信。以下是您可以尝试隔离问题的几件事:

  • 尝试确定这是mysql问题还是java代码问题。尝试使用来自与应用程序服务器相同的主机的命令行工具连接到mysql,并发出类似SQL以执行登录操作。使用一个简单的Java代码进行选择测试,将其部署到同一基础架构中,看看会发生什么等等。还要检查mysql服务器日志,看看是否有任何有用的信息。

  • 空闲连接关闭有两种方式:由运行在应用程序服务器内部的连接池代码或由mysql本身关闭。确保您检查双方的配置。

  • 检查最近是否更改了任何网络基础架构配置。是否有任何新的防火墙规则干扰应用服务器<--> mysql的连通性?是否有任何设置禁止打开TCP连接,空闲时间超过X?

  • 尝试使用不同的连接池库只是为了消除连接池的可能性。

祝好运


在我的情况下,处理这个问题时超时值太小了。 - EdgeCaseBerg

2
可能是防火墙相关问题。

1

我在 PHPStorm 中尝试通过 SSH 连接到 Ubuntu 16.04 虚拟机时遇到了这个错误。我的问题是,在常规选项卡中的“主机”字段上,我把虚拟机的主机名作为值填入,而不是应该填写的 localhost。 (在我的情况下,使用“单连接模式”似乎并没有什么区别。) - jbobbins

1

我曾遇到这个问题,但无法更改MySQL数据库配置。因此,在我的sql连接器类中,我确保在重新初始化之前始终关闭连接。类似于:

public static Connection getConnection() {

    if (DatabaseConnnector.conn == null) {
        initConn();
    } else {
        try {
            DatabaseConnnector.conn.close();          
        } catch (SQLException e) {
            e.printStackTrace();
        }
          initConn();
    }
    return DatabaseConnnector.conn;
}

并且这解决了问题。

0
在我的情况下,问题出在Java MySQL驱动程序/连接器的版本上。即使服务器是8版本,但当我使用MySQL驱动程序/连接器5.1.44版本时,它就开始工作了。

0

可能是服务器连接已满 尝试减少服务器连接或修改最大连接参数


你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

0

我遇到了同样的问题,这是由于锁定时间过长造成的。

我们有一个长时间的事务A,如果执行它,其他事务将被锁定直到事务A完成,因此其他事务总是被我们的mysql工具(pt-kill)杀死。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接