生产问题排查基本命令
当线上服务出现问题的时候, 可以通过固定的步骤, 获取线上环境的信息, 一步一步逐步定位问题所在.
X00. 判断问题影响范围
当突然接到线上报警, 应立即判断问题影响范围,如果直接导致服务不可用,则需立即响应(包括重启服务,或进行服务迁移扩容,正常情况下靠谱的运维不会让这个情况发生).
如果是高可用部署, 则联系运维同事, 切换流量到另外几台正常的机器(修改NGINX配置等),保留作案现场,进行分析定位问题.
X01. 查看cpu
top
- 查看cpu详细信息: 按
1
- 查看负载
- 按cpu使用率排序: 按
P
- 查看COMMAND详细信息: 按
c
- 查看cpu详细信息: 按
top -H -p pid
- 查看某进程下的线程信息
X02. 查看内存
top
- 按内存排序: 按
M
- 按内存排序: 按
free -h
X03. 查看磁盘
df -h
X04. 定位进程号
方法1 通过ps命令
例如: 服务名称为fcrm-c-rest
命令: ps -ef | grep 'fcrm-c-rest'
则可以查询到名称为fcrm-c-rest
的进程号
方法2 通过top命令
敲击top
命令后, 输入M
或 P
分别根据内存使用量排序, 和CPU使用量排序来进行定位线程号
方法3 通过jps命令
通过jps 命令获取当前执行的java进程
jps - Lists the instrumented Java Virtual Machines (JVMs) on the target system. This command is experimental and unsupported.
X05. 查看线程堆栈
jstack pid
- 使用
arthas
中的thread
指令
X06. 查看堆信息
jmap -heap pid
arthas
中的jvm
指令jmap -histo:live pid
查看存活对象信息
X07. 查看GC
jstat -gcutil pid 1s
每1秒打印一次gc信息arthas
中的jvm
指令
实战经历
以下项目均为救火….
线上服务器磁盘空间占满,导致文件上传失败问题
背景: 旧项目因客户机房运维资源不足, 机器维护不及时磁盘空间占满.
问题现象: 突然客户反映列表导出异常, 无法正常导出数据.
排查:
- 登录线上服务器, 查看cpu资源, 正常
- 查看异常日志信息,发现某时刻后的所有日志都没有保留
- 查看磁盘信息, 发现磁盘空间已满. 定位问题.
解决:
- 删除无用文件, 扩容磁盘
- 排查日志打印方式. 减少无用日志输出.
复盘:
- 客户服务器磁盘空间太小-> 只有80G
- 缺少完善的监控体系
- 日志配置有问题,可以设置日志存留时间
- 缺少定时打包日志
线上数据库表缺少索引, 导致更新时,锁全表,数据库连接占盘,tomcat线程打满,拒绝请求
背景: 发送微信模板消息后, 微信有发送回调. 可以处理模板消息发送情况.
问题现象: 客户发送模板消息, 20万条, 服务立马拒绝各种请求, 但是模板消息正常发送.微信登录等C端请求全部失效.
排查:
1. 登录线上服务器, 查看cpu资源, 正常.
2. 查看内存信息, 正常
3. 日志一直报错2020-05-26 11:00:58 [ERROR]-[http-nio-62550-exec-172]-[F93D2D5C-A37D-4267-B0A9-097324554882]-[com.mklinfo.rest.handler.RestExceptionHandler.handleException(RestExceptionHandler.java:75)] GlobalExceptionHandler handleException error org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:321) at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:284) at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:118) at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:297) at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141) at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229) at org.springframework.util.StreamUtils.copy(StreamUtils.java:119) at org.springframework.http.converter.StringHttpMessageConverter.writeInternal(StringHttpMessageConverter.java:106) at org.springframework.http.converter.StringHttpMessageConverter.writeInternal(StringHttpMessageConverter.java:41) at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:228) at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:247) at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:174) at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:113) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:849) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:760) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872) at javax.servlet.http.HttpServlet.service(HttpServlet.java:707) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
怀疑是文件服务数被打满, 使用arthas
的jvm
指令查看文件打开数, 发现略多但也属于正常范围
4. 使用arthas
的 thread
指令, 发现tomcat
的http-nio-62550-exec-XX
线程被打满, 默认200个
5. 使用 jstack
命令, 发现很多线程阻塞在一个地方,查看代码发现阻塞在update,都在等待druid
数据库连接
6. 联系DBA
查看数据库情况, 发现锁表. 表中没有索引.
7. 问题原因: 因发送模板消息, 数据量大, 微信回调更新状态, 表中无索引,导致锁表,更新速度慢, 导致Tomcat线程被占满, 拒绝请求.
解决: 1. 增加数据库索引 2. 增加数据库连接数量 复盘: 1. 根本原因是数据库没有索引导致 2. druid 连接池线程数设置太小: 初始线程10,最大20 -> 调整为初始20,最大50
总结
- 注意
jmap -dump:format=b,file=filename pid
命令,转储堆文件,线上不要轻易使用, 在大内存情况下,会导致服务无法提供服务 - 推荐使用
[arhtas](https://github.com/alibaba/arthas/blob/master/README_CN.md)