该答案适用于Apache tomcat-jdbc数据源提供程序。
首先,您需要了解PoolProperties
setRemoveAbandonedTimeout
setRemoveAbandoned
当查询花费的时间超过setRemoveAbandonedTimeout(int)中指定的时间时,执行此查询的连接将标记为Abandon,并调用java.sql.Connection.close()方法,它将在释放连接之前一直等待查询完成。
。
我们可以实现自己的处理程序来处理废弃的连接。以下是变化
首先我们需要添加一个接口
package org.apache.tomcat.jdbc.pool;public interface AbandonedConnectionHandler { public void handleQuery(Long connectionId);}tomcat-jdbc文件更改:
PoolConfiguration.java (接口)
添加getter和setter方法。
public void setAbandonedConnectionHandler(AbandonedConnectionHandler abandonedConnectionHandler);public AbandonedConnectionHandler getAbandonedConnectionHandler();
将这些方法覆盖到所有实现类
- DataSourceProxy.java
- PoolProperties.java
- org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.java
将方法 getConnectionId() 添加到
org.apache.tomcat.jdbc.pool.PooledConnection.java
public Long getConnectionId() { try { //jdbc impl has getId() Method method = this.connection.getClass().getSuperclass().getMethod("getId"); return (Long)method.invoke(this.connection); } catch (Exception e) { log.warn(" Abandoned QueryHandler failed to initialize connection id "); } return null;}在使用不同的mysql驱动程序的情况下,上述反射代码可能会有所不同。
现在我们需要在org.apache.tomcat.jdbc.pool.ConnectionPool.java中调用java.sql.Connection.close()方法之前放置处理程序。
将启动废弃的连接清除程序的ConnectionPool.java方法是
protected void abandon(PooledConnection con)
在调用 release(con) 之前,在此方法内添加以下代码 ;
if(getPoolProperties().getAbandonedConnectionHandler() != null) { con.lock(); getPoolProperties().getAbandonedConnectionHandler().handleQuery(con.getConnectionId()); }现在,您要做的就是在创建tomcat-jdbc数据源时将handerInstance与PoolProperties一起传递。
p.setAbandonedConnectionHandler(new ConnectionHandler(true));
这是我的AbandonedConnectionHandler实现。
import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import org.apache.juli.logging.Log;import org.apache.juli.logging.LogFactory;import org.apache.tomcat.jdbc.pool.AbandonedConnectionHandler;import org.apache.tomcat.jdbc.pool.PoolConfiguration;public class ConnectionHandler implements AbandonedConnectionHandler{ private static final Log log = LogFactory.getLog(ConnectionHandler.class); private Boolean isAllowedToKill; private PoolConfiguration poolProperties; public ConnectionHandler(Boolean isAllowedToKill) { this.isAllowedToKill = isAllowedToKill; } @Override public void handleQuery(Long connectionId) { Connection conn = null; Statement stmt = null; if(this.isAllowedToKill) { try{ Class.forName(poolProperties.getDriverClassName()); conn = DriverManager.getConnection(poolProperties.getUrl(),poolProperties.getUsername(),poolProperties.getPassword()); Statement statement = conn.createStatement(); ResultSet result = statement.executeQuery("SELECt ID, INFO, USER, TIME FROM information_schema.PROCESSLIST WHERe ID=" + connectionId); if(result.next()) { if(isFetchQuery(result.getString(2))) { statement.execute("Kill "+connectionId); } } statement.close(); conn.close(); } catch(Exception e) { e.printStackTrace(); } finally { try { if(stmt != null && !stmt.isClosed()) stmt.close(); } catch (SQLException e) { log.warn("Exception while closing Statement "); } try { if(conn != null && !conn.isClosed() ) conn.close(); } catch (SQLException e) { log.warn("Exception while closing Connection "); } } } } private Boolean isFetchQuery(String query) { if(query == null) { return true; } query = query.trim(); return "SELECT".equalsIgnoreCase(query.substring(0, query.indexOf(' '))); } public PoolConfiguration getPoolProperties() { return poolProperties; } public void setPoolProperties(PoolConfiguration poolProperties) { this.poolProperties = poolProperties; }}


