忙碌的工作讓我很久沒有寫過博客了,另外技術上也沒有明顯的進步,也缺乏興奮點。項目正式上線很長時間,隨要訪問壓力的增大,每天PV差不多有500萬。這時出現了一些問題,主要的是訪問響應慢,程序中未發現異常。錯誤日志有數據庫連接的錯誤,懷疑是數據庫連接丟失或有某些請求會鎖表。因此寫一段代碼跟蹤數據庫連接獲取和釋放的情況。
原理很簡單,攔截DataSource的getConnnection方法,把當前Connection和調用堆棧保存到連接列表;攔截Connection對象close方法,把Connection從連接列表中刪除。
直接上代碼:
package com.emagsoftware;
import java.lang.reflect.Method;
import java.sql.Connection;
import javax.sql.DataSource;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.apache.log4j.Logger;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* DataSource connection監控處理
*
* @author huzl
* @date 2010-11-26 9:42:17
*/
class DataSourceBeanPostProcessor implements BeanPostProcessor {
public static Map connections = new ConcurrentHashMap();
public static Map connectionTime = new ConcurrentHashMap();
Logger log = Logger.getLogger(DataSourceBeanPostProcessor.class);
public Object postProcessBeforeInitialization(Object object, String name) {
return object;
}
//創建DataSource或DataSource工廠的代理
public Object postProcessAfterInitialization(Object object, String name) throws org.springframework.beans.BeansException {
if (!"dataSource".equals(name)) return object;
System.out.println("****************DataSource postProcessAfterInitialization success ");
if (object instanceof FactoryBean)
return createDataSourceFactoryProxy((FactoryBean) object);
else
return createDataSourceProxy((DataSource) object);
}
private FactoryBean createDataSourceFactoryProxy(final FactoryBean factoryBean) {
if (Enhancer.isEnhanced(factoryBean.getClass())) return factoryBean;
MethodInterceptor factoryInterceptor = new MethodInterceptor() {
public java.lang.Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws java.lang.Throwable {
Object result = method.invoke(factoryBean, args);
if ("getObject" != method.getName()) return result;
return createDataSourceProxy((DataSource) result);
}
};
return (FactoryBean) createProxy(FactoryBean.class, factoryInterceptor);
}
//攔截DataSource getConnection方法,記錄獲取的數據庫連接
private DataSource createDataSourceProxy(final DataSource dataSource) {
if (Enhancer.isEnhanced(dataSource.getClass())) return dataSource;
MethodInterceptor dataSourceInterceptor = new MethodInterceptor() {
public java.lang.Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws java.lang.Throwable {
Object result = method.invoke(dataSource, args);
if ("getConnection" != method.getName()) return result;
connections.put(result, new Exception());
connectionTime.put(result, new java.util.Date());
System.out.println("****************DataSource Connection get size = " + connections.size());
return createConnectionProxy((Connection) result);
}
};
return (DataSource) createProxy(DataSource.class, dataSourceInterceptor);
}
//攔截Connection close方法,清除釋放的數據庫連接
private Connection createConnectionProxy(final Connection conn) {
if (Enhancer.isEnhanced(conn.getClass())) return conn;
MethodInterceptor connectionProxy = new MethodInterceptor() {
public java.lang.Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws java.lang.Throwable {
if ("close" == method.getName()) {
connections.remove(conn);
connectionTime.remove(conn);
System.out.println("****************DataSource Connection close size = " + connections.size());
}
return method.invoke(conn, args);
}
};
return (Connection) createProxy(Connection.class, connectionProxy);
}
private Object createProxy(Class targetInterfaceClass, MethodInterceptor interceptor) {
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(new Class[]{targetInterfaceClass});
enhancer.setCallback(interceptor);
return enhancer.create();
}
}
spring配置文件
<bean class="com.emagsoftware.DataSourceBeanPostProcessor"/>
寫一個頁面,檢查connections和connectionTime中的對象即可
<%@ page import="org.apache.commons.lang.exception.ExceptionUtils" %>
<%@ page import="java.util.Iterator" %>
<%@ page import="com.emagsoftware.DataSourceBeanPostProcessor" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>連接池查詢</title>
<style type="text/css">
.hidden{
display: none;
}
</style>
</head>
<body>
<h1>連接池(<%=DataSourceBeanPostProcessor.connections.size()%>)</h1>
<%
if(DataSourceBeanPostProcessor.connections.size()>0){
out.println("時間:" + DataSourceBeanPostProcessor.connectionTime.values() + "
");
Iterator iterator = DataSourceBeanPostProcessor.connections.values().iterator();
while(iterator.hasNext())
{
Exception ex = (Exception) iterator.next();
out.println("<pre>" + ExceptionUtils.getFullStackTrace(ex) + "</pre>
");
}
}
%>
</body>
</html>
已有 0 人發表留言,猛擊->>這里<<-參與討論
ITeye推薦