问题一:线程死锁
刚进入到首页,或者刷新一下,就一直爱的魔力转圈圈。
排查法:
(一)、请求未发送到服务器
1、负载机问题(cpu、磁盘、内存)
2、网络问题
(二)、请求发送到服务器,可能是服务器处理或者返回过程出现问题
1、应用服务器cpu、磁盘、内存、网络
2、数据库服务器cpu、磁盘、内存、网络
3、数据库连接池排队
4、应用程序代码问题
5、sql语句执行慢,效率低
6、JVM堆栈溢出,频繁gc
7、中间件的线程池排队
排查过程:
(一)负载机问题最好排查,访问以下tomcat的初始页面就行,下图可以访问,从这张图片,可以侧面论证,我们的负载机是没有问题的,最起码可以访问服务器,网络、服务器的tcpip连接(服务器响应了)没有问题,同时也说明 web 容器的连接池并没有满,因为可以访问我们的 81 端口,那么尝试从其他方面去考虑,看看 jvm 和线程栈。
单个排查,如何排查??
1、负载机:随便访问一个页面,比如百度:请求可以访问,证明负载机发送请求是没有问题的。
2、负载机ping服务器可以ping通,查看网络也是没有问题的。
3、服务器的tcp,ip连接:netstat。
4、web容器排队:监控tomcat的连接池是否有空闲线程。server status
(二)请求发送到服务器,可能是服务器处理或者返回过程出现问题
1、cpu:top,负载没有问题,cpu使用率没有问题。
2、线程死锁:jstack pid >1.log
3、gc(oom):jstat -gcutil pid 1000
4、代码
5、查看文件句柄 lsof uminit -n 最大文件句柄。
5、数据库连接池:show processlist 和数据连接池的配置文件,查看是否到达最大连接。
6、数据库死锁:
7、慢查询(开启慢查询,查看慢查询日志)
这里是线程死锁问题: jstack 8071 > 1.log
打开1.log日志,我们可以发现,在 http-nio-8082-exec-XXX 这种的线程中状态几乎都为 blocked,说明所有的 nio 的线程锁住了,没有可供使用的线程都被死锁了,代表请求发送到服务器,没有线程可供处理。
我们看到这里,大概可以判断到应该是线程栈死锁导致的,而且可以看到在锁住的线程调用的方法的路径以及方法名是:org.tarena.common 路径下的 DbUtil类 路径下的 getConnection 方法的 43 行(DbUtil.java:43)
我们切换到该路径下:这里不可以直接 vi ,将文件 down 到本地利用 jd-gui 反编译 .class文件成为一个 .java 文件。
# pwd
/opt/tomcat7/webapps/dangdang/WEB-INF/classes/org/tarena/common
# ll
total 36
-rw-r--r-- 1 root root 737 Jun 24 2016 BooToStrUtil.class
-rw-r--r-- 1 root root 338 Jun 24 2016 Constant.class
-rw-r--r-- 1 root root 2697 Jun 24 2016 CookieUtil.class
-rw-r--r-- 1 root root 371 Jun 24 2016 DangException.class
-rw-r--r-- 1 root root 2954 Jun 24 2016 DbUtil.class
-rw-r--r-- 1 root root 1397 Jun 24 2016 DegistUtil.class
-rw-r--r-- 1 root root 440 Jun 24 2016 EmailUtil.class
-rw-r--r-- 1 root root 2998 Jun 24 2016 ImageUtil.class
-rw-r--r-- 1 root root 2237 Jun 24 2016 VerifyUtil.clas
打开反编译文件,显示如下代码:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.tarena.common; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; public class DbUtil { private static Object ds = new Object(); private static Object connLocal = new Object(); private static String driver; private static String url; private static String dbUser; private static String dbPwd; public static int maxConnection; public static int countConnection; static { Properties pro = new Properties(); try { pro.load(DbUtil.class.getClassLoader().getResourceAsStream("dbcp.properties")); url = pro.getProperty("url"); driver = pro.getProperty("driverClassName"); dbUser = pro.getProperty("username"); dbPwd = pro.getProperty("password"); maxConnection = Integer.parseInt(pro.getProperty("maxActive")); Class.forName(driver); } catch (Exception var2) { var2.printStackTrace(); } } public DbUtil() { } public static Connection getConnection() throws SQLException { synchronized(ds) { Connection connection = null; try { synchronized(connLocal) { connection = DriverManager.getConnection(url, dbUser, dbPwd); System.out.println(countConnection + ":" + maxConnection + ":" + (countConnection > maxConnection)); if (countConnection > maxConnection) { throw new RuntimeException(); } ++countConnection; System.out.println(countConnection); } } catch (SQLException var4) { var4.printStackTrace(); } return connection; } } public static void closeConnection(Connection conn) throws SQLException { synchronized(connLocal) { if (conn != null) { synchronized(ds) { System.out.println("->cloase"); } } } } }
这里是因为加了一个同步锁:synchronized(ds) ,要解决把这里删掉就成了。
线程同步锁导致的线程死锁,这里把同步锁去掉,替换新的class文件就好了。
路径:WEB-INF/classes/org/tarena/common
问题二:堆溢出
1、压测这个接口: http://192.168.0.38:8080/dangdang/user/image.action 也就是首页获取验证码图片的接口。
2、压测过程中,top观察现象——cpu高看线程
# top top - 17:14:41 up 8 days, 5:15, 7 users, load average: 0.22, 0.05, 0.02 Tasks: 113 total, 2 running, 111 sleeping, 0 stopped, 0 zombie Cpu(s): 99.3%us, 0.4%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.4%si, 0.0%st Mem: 2054084k total, 1978048k used, 76036k free, 135632k buffers Swap: 0k total, 0k used, 0k free, 724640k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 8591 root 20 0 2551m 640m 14m S 99.0 31.9 3:50.78 java
cpu和负载较高
查看cpu较高的进程下的线程,看这个线程在执行干啥
把线程id转化为16进制,然后使用jstack pid | grep 【16进制的】
查看到内存溢出这块来。
3、访问8080端口,看是否能访问,这里根本访问不了,cpu和负载有问题,这里也能看出是OOM问题。
或者查看一下gc情况:full gc 次数夸张
# jstat -gcutil 4232 1000 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 100.00 0.00 100.00 100.00 96.53 91.74 71 0.944 2485 269.686 270.630 100.00 0.00 100.00 100.00 96.53 91.74 71 0.944 2495 271.024 271.967 100.00 0.00 100.00 100.00 96.53 91.74 71 0.944 2503 272.043 272.986 100.00 0.00 100.00 100.00 96.53 91.74 71 0.944 2513 273.024 273.967 100.00 0.00 100.00 100.00 96.53 91.74 71 0.944 2523 274.110 275.053 100.00 0.00 100.00 100.00 96.53 91.74 71 0.944 2533 275.096 276.040 100.00 0.00 100.00 100.00 96.53 91.74 71 0.944 2541 276.077 277.020
那么,我们有两种方式去分析堆内存溢出
(4)jmap -histo:live pid
(5)jmap -dump:live,format=b,file=heap.bin pid
4、[root@wuzm ~]# jmap -histo:live 4232 > error.log
这里发现前20没有认识的类和方法,所以这里定位不到类,所以要dump下来文件。
5、[root@wuzm ~]# jmap -dump:live,format=b,file=heap.bin 4232,然后用mat分析,可以看到如下这个类方法里出了问题。
问题三:栈溢出
点击我的当当,查看页面显示:这里为栈溢出,同时显示了类的路径以及名字