Tomcat连接池满分析

本文介绍了一种数据库连接池故障的排查方法,包括添加请求时间过滤器、使用日志进行Excel分析、通过JVisualVM进行线程Dump以及利用监控工具进行实时监控。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

"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.优化一下请求方式,不要调用那么多次数,或者看看具体卡到哪一行代码了,具体问题具体分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值