SpringFramework / JdbcTemplate:事务回滚时{thread}来自threadlocal的数据库连接不是cleanedup

时间:2017-01-25 19:39:47

标签: java spring jdbc spring-jdbc spring-transactions

在生产中,我们看到Spring Framework(Spring-jdbc 4.2.4)没有清理它存储在本地线程中的数据库连接。

相同线程的后续使用导致jdbcTemplate从threadlocal获得可能过时的连接(一个终止了我的mysql服务器) 而不是从连接池中获取新的。发生这种情况时,我们会在连接上出现Broken pipe或通信链路故障。

WrappedException: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 80,416 milliseconds ago.  The last packet sent successfully to
 the server was 1 milliseconds ago.     [source:]

or

at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1038)
at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3621)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2429)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2594)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2541)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2499)
at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1432)
at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3617)
... 336 more
Caused by: java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109)
at java.net.SocketOutputStream.write(SocketOutputStream.java:153)

使用以下代码

可以在单个线程上轻松复制问题

在mysql服务器上设置连接超时,比如60秒

mysql>设置全局wait_timeout = 60

在java代码中

mainMethod() {

  try {

    method1() ;

   } catch(Exception e) {

    method2() // this is the real call
    sleep(80secs)
    method2() // this is there to a simulate another request on same thread

   }


}

@Transactional
method1() {

    jdbcTemplate.execute("select 1") ;
    throw new RuntimeException() ;


}


@Transactional
method2() {

    jdbcTemplate.execute("select 1") ;

}

第二次调用method2是在第一次调用method2泄漏数据连接到threadLocal之后在同一个线程上模拟另一个请求

在调试代码时,我观察到jdbcTemplate调用DataSourceUtils.getConnection来获取连接。

DataSourceUtils.getConnection首先尝试从ThreadLocal获取连接。它只在那里进入dataSource 在threadLocal中没有连接。在这种情况下,它会在threadLocal中找到一个终止(由于超时)连接,这会导致Method2 使用jdbc Broken管道插座异常失败。

将method2更改为@Transactional(propagation = Requires_NEW)没有帮助。

这是一个SpringFramework错误还是应该以不同的方式做某事?应该如何完成method2(对method2的第一次调用),以便在threadLocal上清除其数据库连接

0 个答案:

没有答案