Merge remote-tracking branch 'zhb/master'

This commit is contained in:
zhouhaibin
2012-08-13 08:45:45 +08:00
7 changed files with 361 additions and 48 deletions
+6
View File
@@ -343,6 +343,12 @@
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-mock</artifactId>
<version>2.0.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
@@ -29,18 +29,26 @@ import com.alibaba.druid.util.ServletPathMatcher;
public class WebStatFilter implements Filter {
private final static Log LOG = LogFactory.getLog(WebStatFilter.class);
public final static String PARAM_NAME_SESSION_STAT_ENABLE = "sessionStatEnable";
public final static String PARAM_NAME_SESSION_STAT_MAX_COUNT = "sessionStatMaxCount";
public static final String PARAM_NAME_EXCLUSIONS = "exclusions";
private WebAppStat webAppStat = null;
private WebStatFilterContextListener statFilterContextListener = new WebStatFilterContextListener();
public final static int DEFAULT_MAX_STAT_SESSION_COUNT = 1000 * 100;
private final static Log LOG = LogFactory.getLog(WebStatFilter.class);
private WebAppStat webAppStat = null;
private WebStatFilterContextListener statFilterContextListener = new WebStatFilterContextListener();
/**
* PatternMatcher used in determining which paths to react to for a given request.
*/
protected PatternMatcher pathMatcher = new ServletPathMatcher();
protected PatternMatcher pathMatcher = new ServletPathMatcher();
private final String EXCLUSIONS = "exclusions";
private Set<String> excludesPattern;
private boolean createSession = false;
private boolean sessionStatEnable = true;
private int sessionStatMaxCount = DEFAULT_MAX_STAT_SESSION_COUNT;
private boolean createSession = false;
private String contextPath;
@@ -86,6 +94,7 @@ public class WebStatFilter implements Filter {
if (sessionStat != null) {
sessionStat.setLastAccessTimeMillis(startMillis);
sessionStat.reacord(nanoSpan);
sessionStat.incrementRequestCount();
}
}
@@ -94,10 +103,14 @@ public class WebStatFilter implements Filter {
}
public WebSessionStat getSessionStat(HttpServletRequest request) {
if (!isSessionStatEnable()) {
return null;
}
WebSessionStat sessionStat = null;
String sessionId = getSessionId(request);
if (sessionId != null) {
sessionStat = webAppStat.getSessionStat(sessionId);
sessionStat = webAppStat.getSessionStat(sessionId, true);
}
if (sessionStat != null) {
@@ -140,10 +153,10 @@ public class WebStatFilter implements Filter {
if (excludesPattern == null) {
return false;
}
if (contextPath != null && requestURI.startsWith(contextPath)) {
requestURI = requestURI.substring(contextPath.length());
if(!requestURI.startsWith("/")) {
if (!requestURI.startsWith("/")) {
requestURI = "/" + requestURI;
}
}
@@ -153,7 +166,7 @@ public class WebStatFilter implements Filter {
return true;
}
}
return false;
}
@@ -163,16 +176,41 @@ public class WebStatFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
String exclusions = config.getInitParameter(EXCLUSIONS);
if (exclusions != null && exclusions.trim().length() != 0) {
excludesPattern = new HashSet<String>(Arrays.asList(exclusions.split("\\s*,\\s*")));
{
String exclusions = config.getInitParameter(PARAM_NAME_EXCLUSIONS);
if (exclusions != null && exclusions.trim().length() != 0) {
excludesPattern = new HashSet<String>(Arrays.asList(exclusions.split("\\s*,\\s*")));
}
}
{
String param = config.getInitParameter(PARAM_NAME_SESSION_STAT_ENABLE);
if (param != null && param.trim().length() != 0) {
param = param.trim();
if ("true".equals(param)) {
this.sessionStatEnable = true;
} else if ("false".equals(param)) {
this.sessionStatEnable = false;
} else {
LOG.error("WebStatFilter Parameter '" + PARAM_NAME_SESSION_STAT_ENABLE + "' config error");
}
}
}
{
String param = config.getInitParameter(PARAM_NAME_SESSION_STAT_MAX_COUNT);
if (param != null && param.trim().length() != 0) {
param = param.trim();
try {
this.sessionStatMaxCount = Integer.parseInt(param);
} catch (NumberFormatException e) {
LOG.error("WebStatFilter Parameter '" + PARAM_NAME_SESSION_STAT_ENABLE + "' config error", e);
}
}
}
config.getServletContext().getContextPath();
StatFilterContext.getInstance().addContextListener(statFilterContextListener);
this.contextPath = DruidWebUtils.getContextPath(config.getServletContext());
webAppStat = new WebAppStat(contextPath);
webAppStat = new WebAppStat(contextPath, this.sessionStatMaxCount);
WebAppStatManager.getInstance().addWebAppStatSet(webAppStat);
}
@@ -186,6 +224,30 @@ public class WebStatFilter implements Filter {
}
}
public boolean isSessionStatEnable() {
return sessionStatEnable;
}
public void setSessionStatEnable(boolean sessionStatEnable) {
this.sessionStatEnable = sessionStatEnable;
}
public WebAppStat getWebAppStat() {
return webAppStat;
}
public String getContextPath() {
return contextPath;
}
public int getSessionStatMaxCount() {
return sessionStatMaxCount;
}
public WebStatFilterContextListener getStatFilterContextListener() {
return statFilterContextListener;
}
class WebStatFilterContextListener extends StatFilterContextListenerAdapter {
@Override
@@ -8,44 +8,56 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.alibaba.druid.util.LRUCache;
public class WebAppStat {
private final static Log LOG = LogFactory.getLog(WebAppStat.class);
private final static Log LOG = LogFactory.getLog(WebAppStat.class);
public final static int DEFAULT_MAX_STAT_URI_COUNT = 1000;
public final static int DEFAULT_MAX_STAT_SESSION_COUNT = 1000 * 100;
public final static int DEFAULT_MAX_STAT_URI_COUNT = 1000;
public final static int DEFAULT_MAX_STAT_SESSION_COUNT = 1000 * 100;
private volatile int maxStatUriCount = DEFAULT_MAX_STAT_URI_COUNT;
private volatile int maxStatSessionCount = DEFAULT_MAX_STAT_SESSION_COUNT;
private volatile int maxStatUriCount = DEFAULT_MAX_STAT_URI_COUNT;
private volatile int maxStatSessionCount = DEFAULT_MAX_STAT_SESSION_COUNT;
private final AtomicInteger runningCount = new AtomicInteger();
private final AtomicInteger concurrentMax = new AtomicInteger();
private final AtomicLong requestCount = new AtomicLong(0);
private final AtomicInteger runningCount = new AtomicInteger();
private final AtomicInteger concurrentMax = new AtomicInteger();
private final AtomicLong requestCount = new AtomicLong(0);
private final static ThreadLocal<WebAppStat> currentLocal = new ThreadLocal<WebAppStat>();
private final static ThreadLocal<WebAppStat> currentLocal = new ThreadLocal<WebAppStat>();
private final ConcurrentMap<String, WebURIStat> uriStatMap = new ConcurrentHashMap<String, WebURIStat>();
private final ConcurrentMap<String, WebSessionStat> sessionStatMap = new ConcurrentHashMap<String, WebSessionStat>();
private final ConcurrentMap<String, WebURIStat> uriStatMap = new ConcurrentHashMap<String, WebURIStat>();
private final Map<String, WebSessionStat> sessionStatMap;
private final AtomicLong uriStatMapFullCount = new AtomicLong();
private final AtomicLong uriSessionMapFullCount = new AtomicLong();
private final ReadWriteLock sessionStatLock = new ReentrantReadWriteLock();
private String contextPath;
private final AtomicLong uriStatMapFullCount = new AtomicLong();
private final AtomicLong uriSessionMapFullCount = new AtomicLong();
private String contextPath;
public static WebAppStat current() {
return currentLocal.get();
}
public WebAppStat(){
this(null);
}
public WebAppStat(String contextPath){
this(contextPath, DEFAULT_MAX_STAT_SESSION_COUNT);
}
public WebAppStat(String contextPath, int maxStatSessionCount){
this.contextPath = contextPath;
this.maxStatSessionCount = maxStatSessionCount;
sessionStatMap = new LRUCache<String, WebSessionStat>(maxStatSessionCount);
}
public String getContextPath() {
@@ -101,26 +113,49 @@ public class WebAppStat {
}
public WebSessionStat getSessionStat(String sessionId) {
if (sessionStatMap.size() >= this.getMaxStatSessionCount()) {
long fullCount = uriSessionMapFullCount.getAndIncrement();
return getSessionStat(sessionId, false);
}
if (fullCount == 0) {
LOG.error("sessionStatMap is full");
public WebSessionStat getSessionStat(String sessionId, boolean create) {
sessionStatLock.readLock().lock();
try {
WebSessionStat uriStat = sessionStatMap.get(sessionId);
if (uriStat != null) {
return uriStat;
}
} finally {
sessionStatLock.readLock().unlock();
}
if (!create) {
return null;
}
WebSessionStat uriStat = sessionStatMap.get(sessionId);
sessionStatLock.writeLock().lock();
try {
WebSessionStat uriStat = sessionStatMap.get(sessionId);
if (uriStat == null) {
WebSessionStat newStat = new WebSessionStat(sessionId);
sessionStatMap.putIfAbsent(sessionId, newStat);
uriStat = sessionStatMap.get(sessionId);
if (uriStat == null) {
if (sessionStatMap.size() >= this.getMaxStatSessionCount()) {
long fullCount = uriSessionMapFullCount.getAndIncrement();
if (fullCount == 0) {
LOG.error("sessionStatMap is full");
}
}
WebSessionStat newStat = new WebSessionStat(sessionId);
sessionStatMap.put(sessionId, newStat);
return newStat;
}
return uriStat;
} finally {
sessionStatLock.writeLock().unlock();
}
return uriStat;
}
public void afterInvoke(long nanoSpan) {
@@ -193,19 +228,19 @@ public class WebAppStat {
}
return uriStatDataList;
}
public List<Map<String, Object>> getSessionStatDataList() {
List<Map<String, Object>> uriStatDataList = new ArrayList<Map<String, Object>>(this.sessionStatMap.size());
for (WebSessionStat sessionStat : this.sessionStatMap.values()) {
Map<String, Object> sessionStatData = sessionStat.getStatData();
int runningCount = ((Number) sessionStatData.get("RunningCount")).intValue();
long requestCount = (Long) sessionStatData.get("RequestCount");
if (runningCount == 0 && requestCount == 0) {
continue;
}
uriStatDataList.add(sessionStatData);
}
return uriStatDataList;
@@ -112,6 +112,10 @@ public class WebSessionStat {
}
}
incrementRequestCount();
}
public void incrementRequestCount() {
requestCount.incrementAndGet();
}
@@ -8,12 +8,16 @@ public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = 1L;
private final int maxSize;
public LRUCache(int maxSize){
this(maxSize, 16, 0.75f, false);
}
public LRUCache(int maxSize, int initialCapacity, float loadFactor, boolean accessOrder){
super(initialCapacity, loadFactor, accessOrder);
this.maxSize = maxSize;
}
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return this.size() > this.maxSize;
}
}
@@ -0,0 +1,52 @@
package com.alibaba.druid.bvt.support.http;
import junit.framework.TestCase;
import org.junit.Assert;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import com.alibaba.druid.filter.stat.StatFilterContext;
import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.support.http.stat.WebAppStat;
import com.alibaba.druid.support.http.stat.WebAppStatManager;
public class WebStatFilterTest extends TestCase {
public void test_sessionStatDisable() throws Exception {
MockServletContext servletContext = new MockServletContext();
MockFilterConfig filterConfig = new MockFilterConfig(servletContext);
filterConfig.addInitParameter(WebStatFilter.PARAM_NAME_SESSION_STAT_ENABLE, "false");
WebStatFilter filter = new WebStatFilter();
Assert.assertNull(filter.getWebAppStat());
filter.init(filterConfig);
WebAppStat appStat = filter.getWebAppStat();
Assert.assertFalse(filter.isSessionStatEnable());
Assert.assertTrue(WebAppStatManager.getInstance().getWebAppStatSet().contains(appStat));
Assert.assertTrue(StatFilterContext.getInstance().getListeners().contains(filter.getStatFilterContextListener()));
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain();
Assert.assertNull(filter.getSessionStat(request));
filter.doFilter(request, response, chain);
Assert.assertEquals(0, appStat.getSessionStatDataList().size());
filter.destroy();
Assert.assertFalse(WebAppStatManager.getInstance().getWebAppStatSet().contains(appStat));
Assert.assertFalse(StatFilterContext.getInstance().getListeners().contains(filter.getStatFilterContextListener()));
}
}
@@ -0,0 +1,150 @@
package com.alibaba.druid.bvt.support.http;
import org.junit.Assert;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.mock.web.MockServletContext;
import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.support.http.stat.WebAppStat;
import junit.framework.TestCase;
public class WebStatFilterTest2 extends TestCase {
public void test_lru() throws Exception {
MockServletContext servletContext = new MockServletContext();
MockFilterConfig filterConfig = new MockFilterConfig(servletContext);
filterConfig.addInitParameter(WebStatFilter.PARAM_NAME_SESSION_STAT_MAX_COUNT, "3");
WebStatFilter filter = new WebStatFilter();
filter.init(filterConfig);
WebAppStat appStat = filter.getWebAppStat();
Assert.assertEquals(3, filter.getSessionStatMaxCount());
Assert.assertEquals(0, appStat.getSessionStatDataList().size());
final MockHttpSession session_0 = new MockHttpSession(servletContext);
final MockHttpSession session_1 = new MockHttpSession(servletContext);
final MockHttpSession session_2 = new MockHttpSession(servletContext);
final MockHttpSession session_3 = new MockHttpSession(servletContext);
final MockHttpSession session_4 = new MockHttpSession(servletContext);
// 第一个session请求2次
{
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain() {
public void doFilter(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response) {
((MockHttpServletRequest) request).setSession(session_0);
};
};
filter.doFilter(request, response, chain);
Assert.assertEquals(1, appStat.getSessionStatDataList().size());
Assert.assertEquals(1, appStat.getSessionStat(session_0.getId()).getRequestCount());
}
{
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain() {
public void doFilter(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response) {
((MockHttpServletRequest) request).setSession(session_0);
};
};
filter.doFilter(request, response, chain);
Assert.assertEquals(1, appStat.getSessionStatDataList().size());
Assert.assertEquals(2, appStat.getSessionStat(session_0.getId()).getRequestCount());
}
// 第2个sesion请求1次
{
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain() {
public void doFilter(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response) {
((MockHttpServletRequest) request).setSession(session_1);
};
};
filter.doFilter(request, response, chain);
Assert.assertEquals(2, appStat.getSessionStatDataList().size());
Assert.assertEquals(2, appStat.getSessionStat(session_0.getId()).getRequestCount());
Assert.assertEquals(1, appStat.getSessionStat(session_1.getId()).getRequestCount());
}
// 第3个sesion请求1次
{
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain() {
public void doFilter(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response) {
((MockHttpServletRequest) request).setSession(session_2);
};
};
filter.doFilter(request, response, chain);
Assert.assertEquals(3, appStat.getSessionStatDataList().size());
Assert.assertEquals(2, appStat.getSessionStat(session_0.getId()).getRequestCount());
Assert.assertEquals(1, appStat.getSessionStat(session_1.getId()).getRequestCount());
Assert.assertEquals(1, appStat.getSessionStat(session_2.getId()).getRequestCount());
}
// 第4个sesion请求1次
{
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain() {
public void doFilter(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response) {
((MockHttpServletRequest) request).setSession(session_3);
};
};
filter.doFilter(request, response, chain);
Assert.assertEquals(3, appStat.getSessionStatDataList().size());
Assert.assertNull(appStat.getSessionStat(session_0.getId()));
Assert.assertEquals(1, appStat.getSessionStat(session_1.getId()).getRequestCount());
Assert.assertEquals(1, appStat.getSessionStat(session_2.getId()).getRequestCount());
Assert.assertEquals(1, appStat.getSessionStat(session_3.getId()).getRequestCount());
}
// 第5个sesion请求1次
{
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain() {
public void doFilter(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response) {
((MockHttpServletRequest) request).setSession(session_4);
};
};
filter.doFilter(request, response, chain);
Assert.assertEquals(3, appStat.getSessionStatDataList().size());
Assert.assertNull(appStat.getSessionStat(session_0.getId()));
Assert.assertNull(appStat.getSessionStat(session_1.getId()));
Assert.assertEquals(1, appStat.getSessionStat(session_2.getId()).getRequestCount());
Assert.assertEquals(1, appStat.getSessionStat(session_3.getId()).getRequestCount());
Assert.assertEquals(1, appStat.getSessionStat(session_4.getId()).getRequestCount());
}
}
}