关于c3p0连接池线上Bug的排查
问题描述
在一个很突然的时刻,某一台应用服务器挂掉了。该台服务器承载的是一个单体应用,并发量也很小,下面开始排查…
查看服务器进程
①通常在linux服务器上我们查看进程使用的是ps -ef | grep tomcat(使用tomcat作为应用服务器)
②同样也可以使用 jps -l 命令查看当期正在运行的java进程
tomcat输出日志查看
我们看到进程是正常的,接下来查看日志输出情况
进入到tomcat/logs目录下
输入命令 tail -f catalina.out查看当前是否有日志输出
java.io.IOException:打开的文件过多
at java.lang.UNIXProcess.forkAndExec(Native Method)
at java.lang.UNIXProcess.(UNIXProcess.java:54)
at java.lang.UNIXProcess.forkAndExec(Native Method)
at java.lang.UNIXProcess.(UNIXProcess.java:54)
at java.lang.Runtime.execInternal(Native Method)
at java.lang.Runtime.exec(Runtime.java:551)
at java.lang.Runtime.exec(Runtime.java:477)
at java.lang.Runtime.exec(Runtime.java:443)
查看当前打开文件大小
lsof -p 直接查看打开的文件
lsof -p | wc -l 查看打开文件的数量 (65560)
然后我们可以查看一下当前服务器支持的最大文件打开数量:
ulimit -a
这里好像已经没有设置最大打开文件数量的空间了
日志分析
我们将服务器的运行日志拷贝到本地,截取服务器挂掉的那个时间段,进行一波日志查看
这里显示这个web应用的实例已经停止,为了调试以及试图终止导致非法访问的线程,将引发以下堆栈跟踪。
通过C3P0PooledConnectionPoolManager[identityToken->10q1nwma11dqw4ut1ya84pd|3384a844, dataSourceName->oracle]-HelperThread-#2这条信息,假象是c3p0连接池出了问题,拿不到连接导致应用停止。
查看c3p0配置信息
到这里仍然没有问题,那会不会是工具类的问题呢?
jdbcutils工具类查看
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
public class JdbcUtils {
private static Properties properties = null;
public static Connection getOConnection() {
//通过标识名来创建相应连接池
ComboPooledDataSource dataSource = new ComboPooledDataSource("oracle");
/*Connection con = dataSource.getConnection();
return con;*/
try {
return dataSource.getConnection();
} catch (Exception e) {
// logger.error("Exception in C3p0Utils!", e);
throw new RuntimeException("数据库连接出错!", e);
}
}
//释放连接回连接池
public static void close(Connection conn, PreparedStatement pst, ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
//logger.error("Exception in C3p0Utils!", e);
throw new RuntimeException("数据库连接关闭出错!", e);
}
}
if(pst!=null){
try {
pst.close();
} catch (SQLException e) {
//logger.error("Exception in C3p0Utils!", e);
throw new RuntimeException("数据库连接关闭出错!", e);
}
}
if(conn!=null && conn.isClosed()){
try {
conn.close();
} catch (SQLException e) {
// logger.error("Exception in C3p0Utils!", e);
throw new RuntimeException("数据库连接关闭出错!", e);
}
}
}
}
到这里问题应该是一目了然了。。。
由于每次从c3p0连接池获取连接的时候都会初始化一个dataSource来占用连接池中的一条连接,并且在调用conn.close()之后,只会释放,并不会关闭,所以理论上在调用几次之后,连接池应该已经干涸了。。。
我们将这段代码放到全局中,只要一加载工具类,就初始化一个dataSource就好了
ComboPooledDataSource dataSource = new ComboPooledDataSource(“oracle”);
总结
这个时候一定要会甩锅。。。这个代码不是我写的!!!
然后下次一定不能犯这么蠢的问题。