首页   快速返回

线上故障处理     所属分类 skill
根据原文整理
http://www.rowkey.me/blog/2018/11/22/online-debug/



Java应用突然没有响应、响应缓慢,进程突然消失 意料之外的错误

故障处理思路
第一时间恢复回滚并保留现场
保证服务可用 定时重启、限流、降级等
业务负责人、技术负责人、核心研发人员、架构师、运维工程师以及运营人员对问题的原因进行快速分析。

分析的过程首先要考虑系统近期的变化,包括以下方面
a、系统最近是否有发布? 
b、服务的使用方是否有运营活动? 
c、网络是否有流量波动? 
d、最近的业务量是否上升? 
e、运营人员是否在系统上做了变动? 
f、依赖的基础平台和资源是否进行了发布上线? 
g、依赖的其他系统是否进行了发布?



故障的可能原因
1、代码BUG: 逻辑不严谨、连接未释放 
2、代码性能: 循环外部调用、未使用批量读取、正则循环等 
3、内存泄漏:本地缓存 
4、异常流量/攻击:DDOS 
5、业务量提升:容量预估失误 
6、外部系统问题:数据库、搜索引擎、分布式缓存、消息队列等中间件性能问题,比如CPU、内存、IO指标异常


故障处理三步走
监控 系统及业务监控 
分析
解决 系统、程序参数的调整、代码的重构优化 ,bugfix

故障分析基础知识

1、计算机基础知识:计算机网络、操作系统、计算机组成原理 
2、java内存管理:垃圾回收算法、垃圾回收器、关键GC参数、JVM内存模型等 
3、java代码基准性能测试:可以使用JMH(微基准测试框架)来进行,能够去除JIT热点代码编译对性能的影响 
4、HotSpot虚拟机体系结构
5、系统参数调优
6、掌握常用诊断工具、jdk自带诊断工具以及其他诊断工具的使用
7、了解业务系统:总体架构、压力方向、容量预估、系统相关软件的版本、模式以及参数


常用工具


uptime 

dmesg|tail
查看最新系统日志。常见的OOM kill 和TCP丢包信息

free -m
查看系统内存使用情况
used包含 Buffer和Cache 

top
包含了系统全局的很多指标信息,包括系统负载情况、系统内存使用情况、系统CPU使用情况等

netstat -tanp
查看TCP网络连接情况

vmstat 1
实时性能检测 
包括CPU使用率、内存使用、虚拟内存交换情况、IO读写情况等系统核心指标。
r:等待CPU资源的进程数,这个比平均负载load更能体现CPU的繁忙情况;
b:阻塞在不可中断休眠状态的进程数

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0 2006600 152268      0 1474664    0    0     1    99    0    0  7  2 91  1  0
 1  0 2006600 151972      0 1474672    0    0     0   308 4977 4272 28  2 71  0  0
 1  0 2006600 152096      0 1474676    0    0     0     0 4934 4256 27  1 71  0  0
 1  0 2006600 152064      0 1474676    0    0     0   664 4910 4265 28  1 71  0  0
 4  3 2006600 151148      0 1475408    0    0     0   121 5252 4478 38  3 58  2  0
 
 
 

sysstat
sysstat套件 包含 sar iostat mpstat pidstat等工具。centos系统安装 yum install sysstat 


mpstat -P ALL 1
查看每个CPU的使用情况。如果有一个CPU占用率特别高,说明有可能是一个单线程应用程序引起。

sar -n DEV 1
查看网络设备的吞吐率。 判断网络设备是否已经饱和

sar -n TCP,ETCP 1
查看TCP的连接状态。
active/s  主动发起的连接数目(connect)
passive/s 被动发起的连接数目(accept)
retrans/  重传的数量,能够反映网络状况和是否发生丢包

iostat -xz 1
查看磁盘IO情况。
await(ms):IO操作的平均等待时间,是应用程序在和磁盘交互时,需要消耗的时间,包括IO等待和实际操作的耗时
avgqu-sz:向设备发出的平均请求数量
%util:设备利用率

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           6.96    0.00    1.68    0.78    0.00   90.58
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.01     0.48    0.23    7.39     5.37   383.90   102.23     0.09   13.12   30.51   12.59  12.37   9.42
dm-0              0.00     0.00    0.22    7.84     5.29   383.69    96.54     0.18   21.96   30.75   21.71  11.68   9.41
dm-1              0.00     0.00    0.02    0.03     0.07     0.14     8.00     0.01  117.28   23.19  164.45   2.24   0.01
dm-2              0.00     0.00    0.00    0.00     0.01     0.07    62.20     0.00  719.99   11.50  730.32   2.32   0.00


JDK诊断工具


jstack
堆栈跟踪工具
jmap
java内存映射工具(Java Memory Map)查看堆内存信息 

jhat
java堆分析工具(Java Heap Analysis Tool),用于分析java堆内存中的对象信息

jinfo
java配置信息工具(Java Configuration Information),查看java进程配置信息,也可以动态修改jvm参数

jstat
jvm统计监测工具(JVM Statistics Monitoring Tool),查看JVM的性能统计信息,包括gc统计信息

jcmd
java命令行(Java Command),用于向JVM发送诊断命令请求。
jmap官方标注是unsupported,jcmd可以作为其替代工具

visualvm
通过JMX连接jvm进程,查看JVM的线程、内存、类等信息。可以安装各种插件(通过CATALINA_OPTS开启tomcat jmx接口)

jconsole
功能类似visualvm,可以显示具体的线程堆栈信息以及内存中各个年代的占用情况,并支持直接远程执行MBEAN


greys-anatomy
在线诊断工具,通过动态修改字节码达到无需重启jvm添加日志、监控方法耗时等

arthas
阿里开源的java诊断工具箱,基于greys-anatomy而来,包括在线诊断、反编译字节码、查看最耗资源的java线程等

jwebap
JavaEE性能检测框架,基于ASM增强字节码实现。
支持:Http请求、JDBC连接、method的调用轨迹以及次数、耗时的统计。
二次开发的suishen-jwebap,加入对java8的支持以及redis连接的监控


故障分析思路

1、根据日志输出的异常信息定位问题,需要区分Tomcat中的catalina.out(标准输出和错误)和localhost.xx.log(应用初始化的日志,错误则无法启动) 
2、磁盘是否已满(df -h)?-->删除多余日志 
3、流量是否异常?-->限流、降级、扩展服务节点、架构优化 
4、外部系统问题?-->数据库、搜索引擎、分布式缓存、消息队列的故障解决、性能优化、分区设计等 
5、应用的cpu、内存、IO



CPU分析

无限空循环 耗cpu的纯计算代码 频繁GC 多线程的上下文切换 JIT编译
定位CPU使用率高的线程
top -p PID -H 
jstack PID 打印繁忙进程的堆栈信息 
通过printf %0x PID 转换id为16进制,在堆栈信息中查找对应的堆栈信息 
jstat -gcutil PID 查看GC情况,是否GC引起了CPU飙高 
JVM参数 -xx:+PrintCompliation ,查看是否JIT编译引起CPU飙高

内存分析
频繁GC、响应缓慢;OOM、堆内存、永久代内存、本地线程内存

1、堆外内存:JNI、Deflater/Inflater、DerectByteBuffer。
通过vmstat、top、pidstat等查看swap和物理内存消耗情况。
通过google-perftools来追踪JNI、Deflater这种调用资源的使用状况 

2、堆内存:创建的对象、全局集合、缓存、ClassLoader、多线程 
a、查看JVM内存使用状况:jmap -heap PID 
b、查看JVM内存存活的对象:jmap -histo:live PID  
c、dump heap里所有对象,死和活的:jmap -dump:format=b,live,file=xx.hprof PID 
d、使用eclipse mat或者jhat打开堆dump的文件,根据内存中的具体对象使用情况分析 
e、VJTools中的vjmap可以分代打印出堆内存对象实例占用信息



磁盘IO分析
IO性能差:大量的随机读写、设备慢、文件太大

1、iostat -xz 1 查看磁盘IO情况 
2、 r/s、w/s、rkB/s、wkB/s等指标过大,可能会引起性能问题 
3、await过大,可能是硬件设备遇到瓶颈或者出现故障。一次IO操作一般操作20ms说明磁盘压力过大 
4、avgqu-sz大于1,可能是硬件设备已经饱和 
5、%util越大表示磁盘越繁忙,100%表示已经饱和 
6、通过strace工具定位对文件IO的系统调用

网络IO分析
1、netstat -anpt 查看网络的连接状况。
   当TIMEWAIT或者CLOSEWAIT连接过多时,会影响应用的响应速度。
   前者需要优化内核参数,后者一般是代码BUG没有释放连接 
2、使用tcpdump来具体分析网络IO的数据。tcpdump出的文件直接打开是一堆二进制数据,
   可以使用Wireshark查看具体的连接以及数据的内容。tcpdump -i ech0 -w tmp.cap -tnn dst port 8080 
3、sar -n DEV 查看吞吐率和吞吐数据包数,判断是否超过网卡限制


IO分析tips
1、%iowait 在linux的计算为cpu空闲、并且仍有未完成的IO请求的时间占总时间的比例 
2、%iowait升高并不一定代表IO设备有瓶颈。需要结合其他指标来判断,如await(io操作等待耗时),svctm(io操作服务耗时)等 
3、avgqu-sz是按照单位的平均值,所以不能反映瞬间的IO峰值

cpu使用优化
1、不要存在一直运行的线程(无限循环),可以使用sleep休眠一段时间。
   这种情况普遍存在于一些pull方式消费数据的场景下。
   当一次pull没拿到数据的时候建议sleep一下,再做下一次pull。 
2、轮询的时候可以使用wait/notify机制代替轮询 
3、避免正则表达式匹配、过多的计算。例如避免使用string的format、spilt、replace方法;
   避免使用正则去判断邮箱格式(有时候会造成死循环);
   避免序列化/反序列化 
4、使用线程池,减少线程数以及线程的切换 
5、多线程对于锁的竞争可以考虑减小锁的粒度(使用ReetrantLock)、
   拆分锁(类似ConcurrentHashMap分bukket上锁)或者使用CAS、ThreadLocal、不可变对象等无锁技术。
   此外,多线程代码的编写最好使用JDK提供的并发包、Executors框架以及ForkJoin等,
   此外Disruptor和Actor在合适的场景也可以使用 
6、结合JVM和代码一起分析,避免产生频繁的GC,尤其是Full GC

内存使用优化
1、使用基本数据类型而不是其包装类型能够节省内存 
2、尽量避免分配大对象。大对象分配的代价以及初始化的代价很大,不同大小的大对象可能导致java堆碎片,尤其是CMS 
3、避免改变数据结构大小。如避免改变数组或array backed collections/containers的大小;
   对象构建(初始化)时,最好显式批量定数组大小;改变大小导致不必要的对象分配,可能导致java堆碎片 
4、避免保存重复的string对象。同时也需要小心String.substring()与String.intern()的使用,中间过程会生成不少字符串 
5、尽量不要使用finalizer 
6、释放不必要的引用。ThreadLocal使用完记得释放以防止内存泄漏,各种stream使用完也记得close 
7、使用对象池避免无节制创建对象,造成频繁GC。但也不要随便使用对象池,除非像连接池、线程池这种初始化/创建资源消耗比较大的场景 
8、缓存失效算法,可以考虑使用SoftReference、WeakReference保存缓存对象 
9、谨慎热部署/加载的使用,尤其动态加载类等 
10、打印日志时不要输出文件名、行号,因为日志框架一般都是通过打印线程堆栈实现,生成大量的string。
    此外打印日志时,先判断对应级别的日志是否打开再做操作,否则也会生成大量的string

IO使用优化
1、考虑使用异步写入代替同步写入,可以借鉴redis的aof机制 
2、利用预读取或者缓存,减少随机读 
3、尽量批量写入,减少IO的次数和寻址 
4、使用数据库代替文件存储 
5、使用异步IO、多路复用IO/时间驱动IO代替同步阻塞IO 
6、使用协程提高网络IO性能:Quasar

上一篇     下一篇
elasticsearch5.0索引设置

elasticsearch5.0索引监控

elasticsearch5.0索引状态管理

elasticsearch aerospike kafka副本数设置

kafka副本机制

网络杠精定律