"http-8080-455" - Thread t@684
java.lang.Thread.State: WAITING
at java.lang.Object.wait(Native Method)
- waiting on <3934c6ec> (a org.apache.tomcat.dbcp.pool.impl.GenericObjectPool$Latch)
近期项目多次出现数据库连接不上的问题,初步确定是数据库连接数达到最大,但是按照实际情况计算是远远达不到最大数的,这就考虑到处理请求时间过长导致连接被占用,解决方案如下:
第一步:添加过滤器,抓取各个请求时间保存到日志
package junitParam;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import com.fto.m2.servlet.RequestAttributeKeys;
import com.fto.m2.servlet.RunData;
import com.inqgen.nursing.tools.DateUtils;
public class UsedTimeFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
long t1 = System.currentTimeMillis();
chain.doFilter(request, response);
long t2 = System.currentTimeMillis();
try {
HttpServletRequest req = (HttpServletRequest) request;
RunData rundata = (RunData) request.getAttribute(RequestAttributeKeys.RUNDATA);
Date now = new Date();
String time = DateUtils.format(now, "yyyyMMdd");
File file = new File("c:\\usedtime");
file.mkdir();
FileOutputStream out = new FileOutputStream(new File("c:\\usedtime\\" + time), true);
String s = "";
if (rundata != null) {
if (rundata.getMemberConnection() != null) {
s += rundata.getMemberConnection().getSelf().getLogin();
}
}
String ip = req.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = req.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = req.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = req.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = req.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = req.getRemoteAddr();
}
out.write((DateUtils.format(new Date(), "yyyyMMdd HHmm") + "\tip:" + ip + "\t" + req.getRequestURI() + " usedtime:" + (t2 - t1) + "\t loginId" + s + "\r\n").getBytes());
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void destroy() {
// TODO Auto-generated method stub
}
}
第二步:把文本日志放到excel表格里面去除掉不需要的部分,根据耗时倒序排列,基本能发现调用次数最多且耗时较长的请求,我的表格长这个样子,事故发生时间是下午16:00左右,1550有大量的nursingmain/selectPatientData请求,这个时候可以结合thead hump分析
第三步:打开jvisualvm,通过jmx远程调用(实时性,尽量在事故前后操作),jmx配置方法在另一篇文章中有介绍,切换到线程标签页,点击右侧的线程dump按钮后会在左侧的树节点下生成一个tdump文件
这个文件可以另存,以文本的形式打开
网上有很多可以分析.tdump的工具,也有在线的,不一一介绍
第四步:根据上面表格中找到的调用次数最多耗时最长的请求,在这个线程dump文件中查找,可以定位到具体哪一行代码
也通过probe项目的监控,大概长这个样子,需要跟项目部署在同一个目录,但是Tomcat卡死的时候他也就不行了,所以平时监控可以用用,真出事故了不要太指望
先查看状态标签,发现有个别请求的处理时间达到1个小时以上,要分析代码可以切到线程标签页,复制下请求,在线程页面查找可以定位到具体哪一行代码
1.select * from table;
此sql看起来是没什么问题,如果字段多一些,无非就是*解析的时间长一点,不会有明显差异,但是假设有个别字段特别大,blob类型的,同时表数据量又很大,查询是没什么问题,返回的结果集就难以想象了;
2.优化一下请求方式,不要调用那么多次数,或者看看具体卡到哪一行代码了,具体问题具体分析