Java Web 下彻底解决MySQL 8 小时问题

来源:互联网 发布:怎样注册淘宝号 编辑:程序博客网 时间:2024/06/13 10:50

一、问题的提出


在MySQL数据库中,当一个客户端的连接超过8小时没有向服务器发起任何请求时,连接将被服务端关闭。

而在Java Web中,很多是信息管理性质的系统,工作人员下班后服务器基本上不再向数据库发起请求,因此经常出现8小时后服务器会莫名其妙“死”一阵子的问题


二、解决思路

思路很简单,就是让 Java Web 服务器定向服务器发起一些请求,让这些连接不至于连续8小时都没事干。简而言之,就是给这些数据库连接找些事干。

通常我是让它执行 Select Now() 语句,不用访问数据库磁盘

思路虽然简单,但是实现起来却不是那么简单

1、你得保证连接池中的连接在一定时间范围内被激活一次,当然如果这个连接正在执行访问数据库的操作,则可以不激活

2、为了达到1的目的,就必须使用一个锁机制,确保连接池中所有的连接都被激活一次之后,再把这些连接放回到连接池。否则你连续取到的连接,可能是同一个。

如,你的连接池中有10个连接,如果执行10次循环,每次循环到连接池中取一次连接执行 select now() 查询,然后释放连接,那么你这10次循环可能拿到的都是同一个连接

3、如果为了避免2中的情况发生,把所有的10次连接全部占用完成之后再释放,则有可能出现死锁。


三、程序原理

创建与连接池数量相同的线程,每过一段时间(如1小时)激活这10个线程,每个线程执行一次数据库操作,然后等待2秒,最后进行统一释放连接

这样做,当系统的连接都很空闲时,可保证每个连接都被执行一次;当系统比较忙时,由于每个线程只保持2秒连接,也不会影响系统性能

使用时,在 servlet 中添加一个 ServletContextListener,启动时执行:

public void contextDestroyed(ServletContextEvent sce) {dbHolder.stopHold();}public void contextInitialized(ServletContextEvent sce) {dbHolder = new DBHolder(new DBHolderAction(){private Connection conn = null;@Overridepublic void doAction() throws SQLException {conn = ....// 执行 sql 语句}@Overridepublic void closeDBSession() throws SQLException {try{conn.close();}catch(Exception ex){}finally{conn = null;}}}, 10, // 最大连接数,请从配置文件中读取,这里取固定值60 * 60 // 运行周期);dbHolder.startHold();}



定义的程序代码:

import java.util.*;import org.apache.log4j.Logger;public class DBHolder {private static final Logger logger = Logger.getLogger(DBHolder.class);private boolean holding = false;private Object holdLock = new Object();private int executePeriod;private int maxActive;private DBHolderAction action = null;private Queue<ExecuteThread> threads = new LinkedList<ExecuteThread>();class ExecuteThread extends Thread {private Object locker = new Object();public void run(){if(action == null)return;synchronized(locker){try{long time1 = System.currentTimeMillis();action.doAction();long time2 = System.currentTimeMillis();logger.debug("DBHolder:线程 " + this.getName() + " 访问数据库,共用时" + (time2 - time1) + "毫秒");}catch(Exception e){logger.debug(e.getMessage(), e);}finally{synchronized(threads){threads.add(this);threads.notifyAll();}try{// 一个连接最多保持2秒locker.wait(2 * 1000);}catch(Exception e){}try{logger.debug("DBHolder:线程,开始关闭线程 " + this.getName() + " 的数据库资源");action.closeDBSession();logger.debug("DBHolder:线程,线程 " + this.getName() + " 的数据库资源已经关闭");}catch(Exception e){}}}}public void close(){synchronized(locker){try{locker.notifyAll();}catch(Exception ex){}}}public ExecuteThread(String name){super(name);}}public void startHold(){if(holding == true)return;holding = true;new Thread(){@Overridepublic void run(){while(holding){logger.debug("DBHolder:调度,清理线程");synchronized(threads){while(threads.size() > 0){threads.poll().close();}}logger.debug("DBHolder:调度,清理线程完成,现在创建新的线程,线程数:" + maxActive);for(int i=0; i<maxActive; i++){new ExecuteThread("DBHolder Thread " + (i + 1)).start();}logger.debug("DBHolder:调度,线程启动完成,等待所有线程全部完成数据库访问");synchronized(threads){while(threads.size() < maxActive){try {logger.debug("DBHodler:调度,等待所有线程全部完成数据库访问");threads.wait(1000);} catch (InterruptedException e) {}}}logger.debug("DBHolder:调度,全部线程完成数据库访问,开始释放全部线程的数据库资源");while(threads.size() > 0){threads.poll().close();}logger.debug("DBHolder:调度,全部线程的数据库资源释放完毕");synchronized(holdLock){try{holdLock.wait(executePeriod * 1000L);}catch(Exception ex){}}}}}.start();}public void stopHold(){try{this.holding = false;this.holdLock.notifyAll();}catch(Exception ex){}}/** * 类构造器 * @param action 用于执行数据库访问的接口 * @param maxActive 数据库最大连接数 * @param executePeriod 数据库保持连接操作的运行周期,以秒为单 */public DBHolder(DBHolderAction action, int maxActive, int executePeriod){this.executePeriod = executePeriod;this.maxActive = maxActive;this.action = action;}}


public interface DBHolderAction {public void doAction() throws java.sql.SQLException;public void closeDBSession() throws java.sql.SQLException;}

0 0