一、问题描述
(1)问题现象:tomcat启动比较慢。
(2)简单说明:tomcat的启动慢一般都是跟工程大小以及工程里面jar大小有关系,目前lib里面的jar越大,启动速度越慢。
二、排查步骤说明
2.1确认问题
确认是tomcat问题还是FR工程问题(详细排查过程见2.2),在确认之前,可以进行tomcat和工程的瘦身。瘦身步骤:删去tomcat的log日志和工程里面的以下文件:
- logs:工程日志
- webroot/logs:埋点文件
- webroot/cubes:埋点文件
- webroot/WEB-INF/assets/temp_attach:缓存
- webroot/WEB-INF/assist/clouds:云端运维数据包临时存放目录
- webroot/WEB-INF/schedule:定时调度结果文件
- webroot/WEB-INF/treasures:云端运维数据包文件
通过上面的瘦身工作,如果没有解决,就分开启动排查是否具体由哪个导致。
2.2日志检查
通过刚才的瘦身,如果没有解决问题,新出来的日志一般可能有关键信息,可以对日志进行分析,通过对日志等级和日志里面关键词的检查进行排查,根据关键词。常见的有JDK内存、本身JDK的版本、JVM参数。
2.3数据库性能检查
2.3.1 finedb太大导致启动慢客户那边数据库的性能也会有这方面影响,可以用小工具检查是否数据库稳定,如果finedb太大,可以帮助客户迁移外接数据库。
2.3.2 外置数据库导致启动慢外置数据库驱动不匹配时也可能影响工程启动速度,可以和客户确认外置数据库类型版本与之使用的驱动版本是否匹配。
2.4打堆栈
如果上面的方法都没有用,就需要打堆栈,通过详细的堆栈分析,查找相应的对应原因,比较典型的堆栈分析可以参考4.1
三、详细排查
3.1 确认问题
首先先确认是否是启动慢还是启动失败的问题的问题,这个时候可以从日志入手,因为从前端是看不出来的。在日志里看一下启动的日志,找一下启动结束的标识日志,发现找不到。那说明启动慢或者说启动卡住了。
出现上面信息,就说明是启动完成,不属于启动失败的问题
3.2 检查tomcat
测试是否是工程问题导致的还是tomcat本身问题,可以将工程移开然后进行测试看时间是多少,如果时间特别长,可以确认是tomcat的问题,针对tomcat问题,一般问题集中在于一下几个方面:
(1)域名解析导致的,一般都是检查/etc/hosts文件中没有ipv4和ipv6地址映射,这种一般是加上相关映射即可。详细看案例分析一
(2)tomcat的JVM内存导致的,有一种是xms和xmx设置的一样大导致了启动比较慢,一般可以设置xmx四分之一,调整以后重启,即可解决。
(3)tomcat本身的logs文件大小,一般把这部分删掉然后重启就可以解决
(4)https://blog.csdn.net/njchenyi/article/details/46641141 相关案例,随机数导致的tomcat启动慢和卡住,具体分析可以看该文章,一般都是添加JVM参数就可以解决。-Djava.security.egd=file:/dev/urandom
(5)JDK问题,如果是openJDk可以通过更换成oracle JDK尝试解决。
3.3检查工程问题
通过刚才测试,如果是工程本身的问题,一般有以下几个方面
(1)finedb太大,导致的工程启动慢,一般需要迁移数据库。迁移的数据库的性能也会影响到tomcat启动慢的现象
(2)检查tomcat日志本身,看是否有明显报错。根据报错查找原因,顺便检查其日志本身的等级,如果是debug可以调整成error,并且去日志文件夹检查日志的大小,看是否因为日志本身导致的卡顿,如果太大可以备份删去。
(3)查看performance、logdb、logdbcopy文件夹,有的时候文件过大对启动以及报表预览等都会有影响,如果太大,可以直接备份删去。
(4)通过这些测试,删去以后还是速度慢的话,就可以打堆栈进行处理检查了。使用 /usr/java/jdk1.7.0_79/bin/ cd到java的bin目录下,如果不知道java路径,可使用java -verbose 查看路径使用jstack打堆栈,jstack -l $pid > /home/bjev/1.txt 输出1.txt的路径要为为可读可写的目录。
四、典型案例
4.1 线程分析-地址解析导致
1、通过打堆栈分析线程:查看对应堆栈当中,大量线程解析本地地址的线程,一直没有释放。
一旦看到上面这种大量繁忙线程,并且有inet6Address字眼基本上就是/etc/hosts需要配置本地域名解析。具体操作方法:
linux:生产环境
执行hostname获取主机名.
检查/etc/hosts文件中是否有ipv4和ipv6地址映射
发现没有hostname;ipv4和ipv6的都加上查出来的主机名,改完了之后 ping一下这个主机名, 能通说明改好了;重启一下工程服务,访问速度就正常了。
window环境:
1、首先找到host文件:C:\Windows\System32\drivers\etc然后cmd运行hostname
2、打开host文件:加入主机名即可
路径一般都是上图路径,具体设置跟linux一样
4.2堆栈分析-取数导致
根据上述方法打完堆栈以后可以上传该网站进行分析:https://fastthread.io/
可以先关注线程堵塞,看是否有堵塞线程,一般堵塞线程伴随着cpu内存低,点开堵塞线程,下面的线程是因为报表关闭的时候,等待取数导致的,通过抓取关键词判断堵塞的基本信息
"pool-56-thread-10" #669 prio=5 os_prio=0 tid=0x00007fa7f4005800 nid=0x3ab2c waiting for monitor entry [0x00007fa65545a000]java.lang.Thread.State: BLOCKED (on object monitor)at com.fr.web.core.ReportSessionIDInfor.clearPageSet(Unknown Source)
- waiting to lock <0x00007fae6cd4cf10> (a com.fr.web.core.ReportSessionIDInfor)at com.fr.web.core.TemplateSessionIDInfo.release(Unknown Source)
at com.fr.web.core.ReportSessionIDInfor.release(Unknown Source)
at com.fr.web.core.SessionPoolManager.processCloseSession(Unknown Source)at com.fr.web.core.SessionPoolManager.access$200(Unknown Source)
at com.fr.web.core.SessionPoolManager$5.run(Unknown Source)at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)at java.util.concurrent.FutureTask.run(FutureTask.java:266)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at java.lang.Thread.run(Thread.java:748)
4.3 两种繁忙线程导致的卡顿
4.3.1 线程分析-取数导致取数线程等待数据库返回数据,线程没事做,cpu利用率很低。这个线程状态是Runnable的,从socket缓存区读取数据。主要的特征就是依赖了外部api DruidPooledStatement.executeQuery,如果这种线程比较多,就是取数太慢,可以查看数据库 是否正常。这种一般cup会比较低
"pool-43-thread-1893" #4977 prio=5 os_prio=0 tid=0x00007f624950e000 nid=0x130b7 runnable [0x00007f6150bd3000]java.lang.Thread.State: RUNNABLEat java.net.SocketInputStream.socketRead0(Native Method)at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)at java.net.SocketInputStream.read(SocketInputStream.java:171)at java.net.SocketInputStream.read(SocketInputStream.java:141)at com.sap.db.rte.comm.BasicSocketComm.receiveData(BasicSocketComm.java:494)at com.sap.db.rte.comm.BasicSocketComm.receive(BasicSocketComm.java:582)at com.sap.db.rte.comm.JdbcCommunication.execute(JdbcCommunication.java:116)at com.sap.db.jdbc.ConnectionSapDB.execute(ConnectionSapDB.java:867)at com.sap.db.jdbc.ConnectionSapDB.execute(ConnectionSapDB.java:820)at com.sap.db.jdbc.StatementSapDB.sendCommand(StatementSapDB.java:898)at com.sap.db.jdbc.StatementSapDB.sendSQL(StatementSapDB.java:947)at com.sap.db.jdbc.StatementSapDB.execute(StatementSapDB.java:256)at com.sap.db.jdbc.StatementSapDB.executeQuery(StatementSapDB.java:399)at com.sap.db.jdbc.trace.Statement.executeQuery(Statement.java:184)at com.fr.third.alibaba.druid.pool.DruidPooledStatement.executeQuery(DruidPooledStatement.java:140)at com.fr.data.core.db.dialect.base.key.create.executequery.DialectExecuteQueryKey.execute(Unknown Source)at com.fr.data.core.db.dialect.base.key.create.executequery.DialectExecuteQueryKey.execute(Unknown Source)at com.fr.data.core.db.dialect.AbstractDialect.execute(Unknown Source)at com.fr.data.core.db.dialect.DefaultDialect.executeQuery(Unknown Source)at com.fr.data.impl.AbstractDBDataModel$1.call(Unknown Source)at com.fr.data.impl.AbstractDBDataModel$1.call(Unknown Source)at java.util.concurrent.FutureTask.run(FutureTask.java:266)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at java.lang.Thread.run(Thread.java:745)
4.3.2 线程分析-非IO导致的问题
还有一种就是不涉及IO操作(数据库访问和redis访问都可以认为是IO操作,IO操作会导致cpu空闲)的繁忙线程导致卡顿,下面也有案例分析,下满这个便是该线程一直在scanAttributeValue,导致的卡顿,也是抓关键词查找具体内容分析卡顿因素
"Timer-7" #109 daemon prio=5 os_prio=0 tid=0x00007f8d7cba1000 nid=0x13b4 runnable [0x00007f8d400ef000]java.lang.Thread.State: RUNNABLEat com.fr.third.javax.xml.stream.XMLScanner.scanAttributeValue(XMLScanner.java:548)at com.fr.third.javax.xml.stream.XMLNSDocumentScannerImpl.scanAttribute(XMLNSDocumentScannerImpl.java:499)at com.fr.third.javax.xml.stream.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:298)at com.fr.third.javax.xml.stream.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:1121)at com.fr.third.javax.xml.stream.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:133)at com.fr.third.javax.xml.stream.XMLReaderImpl.next(XMLReaderImpl.java:358)at com.fr.third.javax.xml.stream.XMLEventReaderImpl.nextEvent(XMLEventReaderImpl.java:56)at com.fr.stable.xml.XMLableReader.readXMLObject(Unknown Source)
对你有帮助的话,可以点赞+关注+收藏,更多知识分享持续更新~ 编辑于 2021-6-22 14:15
|