很久没写博客了,项目中用的是pooxl.0.9.1连接池,连接池自带相应的bug已修复
tomcat运行或者reaload之后,tomcat运行出现 如下异常:
严重: The web application [/XXX] registered the JDBC driver [oracle.jdbc.driver.OracleDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
The web application [/XXX] appears to have started a thread named [HouseKeeper] but has failed to stop it. This is very likely to create a memory leak.
刚开始怀疑是tomcat连接池,资源在关闭的时候没有释放,导致内存泄露,tomcat6.0.2X之后,推出一个防止内存泄漏的监听,目录为/conf/server.xml 中
com.xx.dbprovider.DBPooxDriverListener
然后让DBPooxDriverListener实现ServletContextListener接口,在contextDestroyed对所有的驱动进行关闭
public class DBPooxDriverListener implements ServletContextListener {
private ServletContextEvent context=null;
@Override
public void contextDestroyed(ServletContextEvent destoryContext) {
// TODO Auto-generated method stub
Enumeration drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
} catch (SQLException e) {
e.printStackTrace();
}
}
System.out.println("关闭所有数据库连接");
//destroyThreads();
}
@Override
public void contextInitialized(ServletContextEvent context) {
// TODO Auto-generated method stub
context=this.context;
}
/**
* 销毁未正常关闭的线程
*/
private void destroyThreads(){
final Set threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
if(thread.getName().equals("HouseKeeper")){
synchronized (this) {
try {
thread.stop();
return;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
然后发布,重启,关闭,在重启,这样保证手动把未正常关闭jdbc连接,关闭掉,可是运行了几天,又出现了如下错误:
The web application [/XXX] appears to have started a thread named [HouseKeeper] but has failed to stop it. This is very likely to create a memory leak.
这次不报jdbc相关连接未关闭,后来又加上了,如上注释掉的 //destroyThreads();方法,在关闭tomcat的时候,同时关闭掉这个线程,就好了,用jdk自带的工具jvisualvm.exe,在jdk安装目录找到即可,不过需要在tomcat的bin/catalina.bat 配置一下:
set JAVA_OPTS=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9080 -Dcom.sun.management.jmxremote.authenticate=false - Dcom.sun.management.jmxremote.ssl=false
然后tomcat在运行状态,直接双击jvisualvm.exe文件,既可以打开,可以对tomat做相应的监控。
如果如上办法还是不能解决的话,可以尝试做一个守护程序,当tomcat挂掉,或者宕机重启tomcat,不过此方法,不是最好的办法,在这简单说下这个方法,做一个简单页面,然后用URLConnection 定时去连接tomcat服务器,如果返回有内容,说明tomcat正常,如果返回失败,说明tomcat存在异常,需要用到java.lang.Runtime.getRuntime().exec(tomcatRoot+"bin//shutdown.bat"),有的用net start tomcat6 命令,需要把服务注册到系统服务里,操作起来感觉挺麻烦的,直接这样就可以,也不同注册服务,然后开启一个线程,设定一个定时去连接tomcat,一旦发生异常,重启就可以,下面是代码,不过此代码有这样的一个弊端,必须要把tomcat启动后的等待时间设置稍微长一些,否则会导致tomcat连续开启多个tomcat,下面是代码,大家可以交流探讨下:
/**
* tomcat 服务监听守护线程
* @author lt
*
*/
public class TomcatMonitor implements Runnable {
String start=""; //系统命令 启动
String stop=""; //系统命令 关闭
String testHttp=""; //测试连接地址
String tomcatRoot=""; //tomcat根目录
int testIntervalTime=1;//测试连接间隔时间,单位为秒
int waitIntervalTime=2; //等待测试间隔时间,单位为秒
int testTotalCount=5; //测试连接总次数
Thread thread=null;
public TomcatMonitor(){
ResourceBundle config= ResourceBundle.getBundle("config");
try {
stop=config.getString("stop");
start=config.getString("start");
testHttp=config.getString("testHttp");
tomcatRoot=config.getString("tomcatRoot");
testIntervalTime=Integer.parseInt(config.getString("testIntervalTime"));
waitIntervalTime=Integer.parseInt(config.getString("waitIntervalTime"));
testTotalCount=Integer.parseInt(config.getString("testTotalCount"));
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("*******************初始化成功!*******************");
thread=new Thread(this);
thread.start();
}
public void run() {
System.out.println("正在监控中...");
int testCount=0;
while (true) {
testCount=0;
testCount++;
boolean isrun=checkStatus();
System.out.println("正在启动测试连接,尝试连接次数为:"+testCount+",结果为:"+(isrun==false?"失败.":"成功! ")+ getCurrentTime());
//检测状态
while (!isrun) {
if(testCount>=testTotalCount)break;
try {
thread.sleep(testIntervalTime*1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
testCount++;
System.out.println("正在启动测试连接,尝试连接次数为:"+testCount+",结果为:"+(isrun==false?"失败.":"成功! ")+getCurrentTime());
isrun=checkStatus();
}
//先关闭,后启动服务
while (!isrun) {
try {
System.out.println("正在执行关闭......");
Runtime.getRuntime().exec(tomcatRoot+"bin//shutdown.bat");//先执行关闭
thread.sleep(8000);
System.out.println("正在重新启动......");
Runtime.getRuntime().exec(tomcatRoot+"bin//startup.bat");//启动
thread.sleep(waitIntervalTime*1000);
System.out.println("重启tomcat成功");
isrun=checkStatus();//重启后,再次连接看是否正常
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("重启tomcat异常,请查看先关错误信息");
}
}
try {
thread.sleep(waitIntervalTime*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 检查服务器状态
* @return true:运行正常 ;false:服务器发生异常
*/
public boolean checkStatus() {
URL url=null;
InputStreamReader in=null;
try {
url=new URL(testHttp);
URLConnection urlConn=url.openConnection();
urlConn.setConnectTimeout(15000);
urlConn.setReadTimeout(4000);
in=new InputStreamReader( urlConn.getInputStream());
BufferedReader reader = new BufferedReader(in);//实例化输入流,并获取网页代码
String s;
while ((s = reader.readLine()) != null) {
System.out.println("返回值:"+s);
return true;
}
} catch (IOException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
System.out.println(e.getMessage());
return false;
}finally {
if (in!=null) {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("关闭文件流失败****");
}
}
}
return false;
}
public String getCurrentTime() {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(new Date());
}
public static void main(String[] args) throws Exception{
TomcatMonitor tm=new TomcatMonitor();
// tm.run();
// String tomcatRoot="D:/apache-tomcat-6.0.35restart/";
// Process p= java.lang.Runtime.getRuntime().exec(tomcatRoot+"bin//startup.bat");//启动
// java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(p.getInputStream()));
/* String tomcatRoot="D:/apache-tomcat-6.0.35restart/";
Process p= java.lang.Runtime.getRuntime().exec(tomcatRoot+"bin//shutdown.bat");//启动
java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(p.getInputStream()));
String s;
boolean restart = false;
String t = "Server startup in";
while ((s = in.readLine()) != null) {
System.out.println(s);
if (s.indexOf(t) != -1) {
restart = true;
break;
}
}*/
}
}
下面是配置文件:
#测试连接总次数 testTotalCount=3 #连接失败时,再次检测时间间隔,单位为秒 testIntervalTime=3 #连接超时时间,即多少秒tomcat没响应视为宕机,单位为秒 connectionTimeout=15 #tomcat启动时间,防止在tomcat尚未启动完成的时候,程序又去检验tomcat状态,造成尚未启动完成又重新启动,单位为秒 tomcatStartTime=600 #测试连接地址 testHttp=http://127.0.0.1:9080/SpringMvcMQ/login #testHttp=http://192.168.31.156:8080/nhip/enterapp.do?method=begin&name=/ESBManager&welcome=/ESBManager/pages/index.jsp #tomcat根目录 tomcatRoot=D:/apache-tomcat-6.0.35restart/ #正常情况下,每次检测间隔时间,单位为秒 waitIntervalTime=90放到web.xml做监听:
tomcat服务一启动就会执行该守护线程,同时更加明了的看到tomcat的运行状态com.neusoft.nhip.monitor.TomcatMonitorListener index.html