【Developer Log】正确使用C3P0

开发包括使用他人的和写自己的。在使用他人的,网络确实提供了很大的方便,可以很快地抄到很有用的语句。但是对于开发server端,我的建议是使用网络search作为速成,但是要开发上线系统的时候,最好到官网上看一下人家是怎么说的。

项目(web app)需要对所有使用资源进行关闭。tomcat是web container,web项目作为web app运行在web容器中,而不是作为进程独立运行的。deploy web app和关闭进程不一样,不会确保资源的释放和线程的全部结束,必须要写destory的。初学的开发人员很容易忽略,因为使用Eclipse开发中,开发调测都是重启tomcat的,相当于关闭了tomcat的进程。但是你不清楚生产环境的情况,生产环境中部署的可能并不只你一个项目,部署人员可能是通过deploy,而不是关闭tomcat再重启。

虽然一直没有使用过C3P0,但是在项目退出时必须要关闭的。C3P0维护的是数据库连接池,这些连接还要确保长时间没有sql处理,通过心跳维持确保连接不断开,可以判断必须要关闭,这也是在官网中Quick start里明确提到的。不关闭的结果是数据库的连接不断被占,直至占满,除非关闭tomcat。

如何确保deploy时释放所有资源,很简单,看tomcat的log。tomcat是有检查的,如果出现线程没有释放或者对象不能释放,在给出告警的,可以在catalina.out中查看。在Eclipse的console中也可以看到(不要关闭tomcat,否者会终止tomcat和console的联系,不在console中显示)。例如:

01-Jul-2016 12:00:10.360 WARNING [localhost-startStop-3] 
org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads 
The web application [xxxx-cap] appears to have started a thread named [Abandoned connection cleanup thread] 
but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 java.lang.Object.wait(Native Method)
 java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
 com.mysql.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:43)

必须确保在deploy的时候,tomcat不报错,除非你能证明这个错100%不会导致内存泄漏。出现这种情况,通常就是在退出时没有正确关闭和释放所有的资源。下面是我写的一个使用C3P0的Mysql管理器。

public class C3p0MysqlManager {
	private static Logger logger = LogManager.getLogger();	//在上线项目中尽量不要使用System.out.println。
	private static final Map<String,ComboPooledDataSource> dataSourceDb = new HashMap<>();		
	
	/**
	 * 在管理器中加入某个数据源
	 * @param name  数据源的名字
	 * @param jdbcUrl 
	 * @param user
	 * @param pw
	 * @param minPoolSize
	 * @param maxPoolSize
	 * @param poolIncrement
	 * @throws Exception
	 */
	public static void open (String name , String jdbcUrl,String user, String pw, 
			int minPoolSize,int maxPoolSize,int poolIncrement) throws Exception{
		if(dataSourceDb.containsKey(name))
			throw new Exception("DataSource " + name + " already exist, please close first.");
		
		ComboPooledDataSource  dataSource = new ComboPooledDataSource();
		dataSource.setDriverClass("com.mysql.jdbc.Driver");
		dataSource.setJdbcUrl(jdbcUrl);
		dataSource.setUser(user);
		dataSource.setPassword(pw);		
		dataSource.setMinPoolSize(minPoolSize);
		dataSource.setAcquireIncrement(poolIncrement);
		dataSource.setMaxPoolSize(maxPoolSize);
		dataSource.setMaxIdleTime(minPoolSize);		
		dataSource.setInitialPoolSize(minPoolSize);
		
		/* 检查数据源是否有效。C3P0不会在创建dataSource时就去连接,在第一次连接请求时进行。
		* 一方面会增加第一个用户请求的处理时间,影响第一个用户的体验;
		* 另一方面如果密码错误或者地址不可访问,我们需要在项目部署的第一时间知道是否部署正确,而不是等到业务来了才发现。*/
		try{
			Connection conn = dataSource.getConnection();
			conn.close();
		}catch(Exception e){
			DataSources.destroy( dataSource);  // or dataSource.close()
			throw new Exception("DataSource " + name + " open error :" + e.toString());			
		}
				
		dataSourceDb.put(name, dataSource);		
		logger.info("Open DataSource '" + name + "', current connections number is " + dataSource.getNumConnections());
	}
	
	/**
	 * 关闭某个数据源
	 * @param name 数据源名字
	 */
	public static void close(String name){
		ComboPooledDataSource dataSource = dataSourceDb.remove(name);
		if(dataSource != null)
			dataSource.close();		
	}
	
	/**
	 * 在项目结束的时候,应释放所有资源,应调用本方法。建议阅读C3P0官网。
	 */
	public static void destroy(){
		Iterator<Map.Entry<String,ComboPooledDataSource>> iter = dataSourceDb.entrySet().iterator();
		while (iter.hasNext()) {
			Map.Entry<String,ComboPooledDataSource> entry =  iter.next();
			ComboPooledDataSource dataSource = (ComboPooledDataSource)entry.getValue();
			dataSource.close();
		}
		dataSourceDb.clear();		
		try {			
			com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.shutdown();
		} catch (InterruptedException e) {
			logger.error(e);
		}
		
		Enumeration<java.sql.Driver> drivers = DriverManager.getDrivers();
		while(drivers.hasMoreElements()) {
			try {				
				Driver driver = drivers.nextElement();
				DriverManager.deregisterDriver(driver);
			} catch (SQLException e) {
				logger.error("C3M0Manager destroy error : " + e.toString());
			}
		}	
	}
	/**
	 * @param name 数据源的名字
	 * @return 获取某个数据源的连接
	 */
	public static Connection getConnection(String name){
		try {
			ComboPooledDataSource dataSource = dataSourceDb.get(name);
			return dataSource == null ? null : dataSource.getConnection();
		} catch (SQLException e) {
			logger.error(e);
			return null;
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值