线上基础问题排查常用手册
问题分类
业务问题
性能问题
实施手段
日志排查
阿里云
参考: https://help.aliyun.com/document_detail/29060.html?spm=5176.2020520112.2.1.28bb34c0daZmJB
短语查询
查询包含foo的词
"foo"
使用模糊查询
查询包含foo前缀的词
foo*
使用全文查询
查询任何字段中包含foot的日志
foot
使用字段查询
查询message字段包含foot的日志
message:foot
使用数字范围查询
查询status范围在400(包含)到499(包含)
(注意,数值查询,需要把字段定义成long或double类型)
status in [400 499] 或者 status ≥400 and status ≤499
组合查询
查询status范围在400(包含)到499(包含),并且message包含foot
status ≥400 and status ≤499 and message:foot
本地日志
# 本地日志可能非常大,所以正常需要根据需要来过滤数据
# 如果直接打开超大文件,可能会导致一些问题,比如: 假死
# 指定显示行数
cat test.log | tail -n 20 # 显示20行
# 查找包括Exception字符串的行
cat test.log | grep "Exception"
# 指定行数,避免过大
cat test.log | grep "Exception" | tail -n 20
# and 查找包括Exception与java:81
cat test.log | grep "Exception" | grep "java:81"
# or 查找包括Exception或RequestMappingHandlerAdapter
cat test.log | grep -E "Exception|RequestMappingHandlerAdapter"
# 统计相关
# 统计Exception数量
cat test.log | grep "Exception" | wc -l
代码逻辑排查
本地代码
- 单元测试
- 请求回放
- 断点
线上代码
# arthas 下载&安装祥见附录1
# jad 参考: https://arthas.gitee.io/jad.html
# 功能:反编译代码。 可以直观的确认当前运行版本代码
# 解决:提交了,不知道为什么没有作用?
jad com.nascent.ecrpsaas.openapi.interceptor.APIHandlerInterceptor
# 将编译代码输出
jad com.nascent.ecrpsaas.openapi.interceptor.APIHandlerInterceptor > /tmp/APIHandlerInterceptor.java
配置排查
# arthas 下载&安装祥见附录1
# 解决:配置没有生效? 现在有配置是什么样的?
静态配置类&枚举类
# arthas 下载&安装祥见附录1
# ognl 参考: https://arthas.gitee.io/ognl.html
ognl "@com.nascent.ecrpsaas.openplatform.enums.GlobalsEnums@SYSTEM_ERROR.getMsg()"
获取SpringBean对象
比如项目中,配置了一个SpringBean对象
<!-- activeMq的连接池 -->
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
<property name="connectionFactory" ref="targetConnectionFactory"/>
<!--连接池的最大连接数-->
<property name="maxConnections" value="${activemq.pool.max-connections}"/>
</bean>
获取对应的pooledConnectionFactory
当前配置
# 获取任何一个请求对象,因为都会存在对应的SpringContext容器
tt -n 1 -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod
# 特别强调: 这里在正式环境中,添加-n来指定次数,且应该设置成1。
# 否则当你执行一个调用量不高的方法时可能你还能有足够的时间用 CTRL+C 中断 tt 命令记录的过程,但如果遇到调用量非常大的方法,瞬间就能将你的 JVM 内存撑爆。
# 通过tt的回放能力,获取容器内的Bean
# 随意请求一个地址,只需要被Spring拦截
# 如:http://localhost:9091/openApi/sentinel/flowRules
此时,如果使用ognl
可以得到target
(当前被调用对象)。因为这个对象是由SpringContext
管理的,所以可以获取得容器
信息。
tt -i 1003 -w 'target.getApplicationContext().getBean("pooledConnectionFactory")'
# 获取最大连接数
tt -i 1003 -w 'target.getApplicationContext().getBean("pooledConnectionFactory").maxConnections'
接口问题
响应时间
# arthas 下载&安装祥见附录1
# 解决:接口响应慢?哪个环节慢?
# trace 参考: https://arthas.gitee.io/trace.html
trace com.nascent.ecrpsaas.openapi.interceptor.APIHandlerInterceptor preHandle -n 1
# 特别强调: 这里在正式环境中,添加-n来指定次数
# 根据方法用时过滤
trace com.nascent.ecrpsaas.openapi.interceptor.APIHandlerInterceptor preHandle -n 1 '#cost>10'
# 该过滤是过滤整个方法的用时,而不是指过滤方法内的调用方法用时
trace 的问题在于,
- 无法支持重载方法
- 无法直接定位方法下的方法
入参与响应结果跟踪
# arthas 下载&安装祥见附录1
# 解决:调用参数与响应结果跟踪。
# tt 参考: https://arthas.gitee.io/tt.html
# 表达式变量祥见附录2
tt -n 1 -t com.nascent.ecrpsaas.openapi.controller.customer.CustomerController getCustomerInfo4ThirdParty
# 请求参数
tt -i <tt-index> -w 'params'
# 响应结果
tt -i <tt-index> -w 'returnObj'
JVM问题
CPU
查看CPU占用
top
如果CPU
一直过高,并且不见回落。则需要排查进程要关线程的状态。
线程跟踪
# 查看进程内最耗费CPU的线程
top -Hp <pid>
如何排查对应的线程问题? 需要结合jstack
jstack
跟踪所有线程
jstack 1 | more
该命令可以查看出当前JVM
中的线程情况。
主要关注以下三个状态:
• WAITING:进入等待状态
• 方式:wait/join/park方法进入无限等待,通过notify/notifyAll/unpark唤醒;
• TIMED_WAITING:与WAITING类似。
• 方式:a. 给定等待时间的wait/join/park
• 方法;b. sleep方法;
• BLOCKED:被动进入等待状态
• 方式:进入Synchronized块;
线程状态数量统计
jstack 1 | grep "State: WAITING" | wc -l
查看特定线程的状态信息
查看线程Id=65的线程信息
# 转义进程Id为16进制
printf '%x\n' 65 # 41
jstack 1 | grep 41
jstack排查死循环线程Demo
package com.nascent.test.thread;
public class DeadLockTest {
private static final Object obj1 = new Object();
private static final Object obj2 = new Object();
public static void main(String[] args) {
Thread thread = new Thread(() -> {
synchronized (obj1) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
System.out.println("t1");
}
}
});
Thread thread2 = new Thread(() -