知识点参考于 芋道源码文章笔记,对自身提升有帮助,所以手动写下来加深理解
核心内容:正确的处理一次在线故障
故障发生时:意料之外此的错误,无响应或者响应缓慢,但出于服务中,无法关闭影响用户体验,需要尽快修复
应对思路:
重点:第一时间上报给自己的直属领导和相关负责人,并再可能的情况下及时周知问题,影响范围,解决方案,预计恢复时间
有2个捷径
1团队中对相关问题有经验者,并能确定通过某种手段恢复系统的正常运行,那么第一时间恢复(回滚),但同时要保留现场,以备后续问题的定位和处理;如果没有经验,则根据错误程度,粗暴的采用定时重启,限流,降级等保证服务可用
2业务负责人,技术负责人,核心研发,架构,运维和运营对问题快速分析,主要考虑方向是系统近期的变化,分以下几个方面:
1近期是否有新版本发布
2是否有营运活动
3是否有流量波动
4业务量是否有提升
5运营人员是否在系统上做变动
6依赖的基础平台和资源(第三方接口或者maven的jar)是否有新发布
常见的可能原因:网路,CPU,内存,IO指标异常
1.DNS,网路,CDN故障
2.代码BUG,逻辑不严谨,连接未释放
3.代码性能本身问题
4.内存泄漏:本地缓存
5.异常流量:DDOS
6.业务提升导致QPS无法负载
7.数据库,搜索引擎,分布式缓存,消息队等中间件的性能问题
必备准备工作知识(可多人聚合)
1计算机基础知识:网络,操作系统,网络链路
2java内存管理:垃圾回收算法,垃圾回收器,关键GC参数,JVM内存模型
3java代码规范,性能测试流程
4系统参数调优
5常见系统诊断工具,JDK自带的和其他的
6了解业务系统:总体架构,压力方向,容量,软件版本,模式和基础参数
CentOS自带的检测工具
1.uptime:系统负载的任务量
2.demesg|tail:查看系统日志
3.vmstat l :查看CPU的LOading状态
4.free -m 查看内存情况
5.top:系统全局指标信息
6.netstat -tanp:查看TCP网路连接状况
系统诊断工具-sysstat,指令如下
Mpstat -p all l:查询CPU使用情况(一个占用特别高的话可能就是单线程引起的)
Sar -n dev l:查询网络设备吞吐量的
Sar -n tcp,ETCP l:查看TCP连接状态,每秒发起的连接数目,可查网络谁都丢包
Iostat -xz l:查询磁盘的IO情况
JDK诊断工具
·jstack:java堆栈跟踪工具,主要打印指定java进程,核心文件或远程调试器的JAVA线程的堆栈跟踪信息
·jmap:java内存隐射工具,打印java进程,核心文件或远程调试服务器的共享对象内存
·jhat:java堆分析工具
·jinfo:JAVA配置信息根据
·jstat:JVM系统检测工具
.jcmd:java命令行
·visualvm(推荐可使用ideal集成),JVM线程,内存,类等信息
·jconsole:功能和visualvm类似,支持直接远程执行MBEan
·jmc:收费的软件,功能更佳
其他工具
1charies:HTTP协议抓包工具。可将自己设置成系统的网路访问代理服务器,实现网路数据包的截取和分析
2tpacketcapture:TCP协议抓包工具,主要用于android端获取TCP数据包,生成的文件可以通过wireshark分析
3greys-atonomy:在线诊断工具,可通过动态修改字节码达到无须重启JVM添加日志,监测方法耗时等动态增强代码的目的
4arthas(自己有学习,很不错的):阿里开源的java诊断工具箱,基于greys-atonomy而来,可在线诊断,反编译字节,查看最消耗资源的java进程
5jwebap:javaEE性能检测框架,支持http请求,JDBC连接,method的调用轨迹以及次数,耗时的统计
6awesome-scripts:封装了很多常用的诊断工具,脚本
步骤思路(在没有准确的知道原因的情况下)
1.DNS是否正常(ping)
2网路是否正常(ping,telnet)
3查看日志文件(1查看tomcat的cataliana.out初始化日志,然后在看项目日志,此步骤出问题最常见)
4检查磁盘是否满(删除多余日志)
5流量是否有异常?限流,降级,扩散服务节点,架构优化
6外部系统问题:数据库,搜索引擎,分布式缓存,消息队列,性能,分区设计
7应用的CPU,内存,IO
CPU常见分析
使用top,vmstat,ps等指令定位CPU使用率较高的线程:top -p [ processId] -H
Jstack [pid] 打印繁忙进程的堆栈信息
通过printf %ox [processId] 转换进程ID为16进制,在堆栈信息中查找对应的堆栈信息
Jstat -gcutil [pid] ,查看GC的情况是否正常,是否GC引起CPU过高
JVM 加入 -xx:+printCompilaton参数,查看是否是JIT编译引起的CPU飙高
出现原因:线程中有空循环,无阻塞,正则匹配或者单纯的计算,频繁的GC,多线程的上下文切换,JIT编译
小知识
1个进程的CPU使用率是所有线程之和
2top的CPU使用率近似实时,PS则是平均
3top的CPU默认使用率是irix mode,单cpu最大值是100%,多处理器环境下,类如4核%CPU最大值是400%
4jstack查看线程栈要注意:由于jstack dump实现机制每次只能转存一个线程的栈信息,所有会看到一些冲突信息,如一个线程正在等待的锁并没有被其他线程持有,多个线程
持有同一个锁等
内存分析(频繁GC,响应缓慢,oom,堆内存,永久代内存,本地线程内存)
1堆外内存:JNI,Deflater/iinflater,directbyteBuffer.通过vmstat,top,pidstat等查看swaP和物理内存的消耗状况
2堆内存:创建的对象,全局集合,缓存,classloader,多线程
-检查JVM内存使用状况:jmap -heap <pid>
-查看JVM内存存活的对象:jmap -histo:live <pid>
-把heap里所有对象都dump下来,无论对象死活:jmap -dump:format =b,file=xxx.hprof <pid>
磁盘IO分析(大量的随机读写,设备慢,文件太大)
·iostat -xz l 查看磁盘IO情况
·r/s,w/s,rkb/s,wkb/s等指标过大引起的问题
·await过大,硬件设备遇到了瓶颈或出现故障,一次IO操作一般超过20ms就说明磁盘压力过大
·avgqu -sz大于1,可能是硬件设备已经饱和
·%util越大表示磁盘越繁忙,%100表示已经饱和
网路IO分析
·netstat -anpt查看网路连接状况,当TIME_WAIT或者CLOSE_WAIT连接过多时,会影响反应速度,前者需要优化内核参数,后者代码BUG没有释放网络连接
·使用tcpdump来具体分析网络IO的数据,TCPdUMP出来的二进制文件数据可用wireshark查看具体的连接以及其中数据内容。Tcpdump - I eth0 -w tmp.cap -tnn dst port 8080
·sar -n dev,查看吞吐率和吞吐数据包数,是否超过网卡限制
IO分析小知识
·%iowait在linux的计算机为CPU空闲,并且有仍未完成的IO请求的时间占总时间的比例
·%iowait升高不一定代表IO设备有瓶颈,需要结合其他指标来判断,如await,svctm等
·avgqu -sz是按照单位时间的平均值,不能反应瞬时的IO洪水
在线代码分析(重点需要掌握的)
1远程DEBUG:TOMCAT远程调试
2在线Trace:Btrace,houseMD,Greys-atonomy,arthas
故障解决
1代码BUG:FIX
2性能问题:CPU,内存,IO使用优化
3JVM配置
CPU使用优化方法
1不要存在一直运行的线程(无限循环),可使用sleep休眠一段时间。这种情况普遍存在于一些pull方式消费数据的场景下,当一次pull没拿到数据时建议sleep一下,在做下一次
2轮询的时候可以使用wait/notify机制代替循环
3避免正则表达式匹配,过多的计算。类如,避免使用String的format,split,replace方法,避免使用正则判断邮箱格式,避免序列化和反序列化
4使用线程池,减少线程数以及线程切换
5多线程对锁的竞争考虑减少锁的粒度,拆分锁(类似ConcurrentHashMmap分bucket上锁),或者使用CAS,threadlocal,不可变对象等无锁技术,多线程最好使用JDK提供的并发包,EXECUTORS框架,此外Disruptor和actor在合适的场景也能使用
6结合JVM和代码一起分析,避免产生频繁的GC,尤其是Full GC.
内存优化方法
·使用基本数据类型而不是包装类型能够节省内存
·尽量避免分配大对象。大对象分配的代价以及初始化代价很大,不同大小的的大对象可能导致Java堆碎片,尤其是CMS;
·避免改变数据结构大小。避免改变数组集合的大小,对象构建最好指定合适的大小。改变大小导致不必要的对象分配,可能导致JAVA堆碎片。
·避免保存重复的string对象,同时也需要小心String.substring()与string.intern()的使用,中间过程会产生不少字符串
·尽量不适用finalizer
·释放不必要的引用:ThreadLocal使用完记得释放以防止内存泄露,各种stram使用完记得close
·适用对象池避免无节制的创建对象,造成GC,也不要随便使用对象池,除非像线程池,连接池等初始化/创建资源消耗较大的场景
·缓存失效算法
·谨慎热部署/加载使用。尤其是动态加载类
·打印日时不要输出文件名,行号。因为一般日志框架是通过打印线程堆栈实现,生成大量的String,另外,应对打印日志级别进行区分选择合适的
IO优化方法
1多考虑使用异步写入代替同步写入,参考redis的AOF机制
2利用预读取或者缓存,减少随机读
3尽量批量写入,减少IO次数和寻址
4使用数据库代替文件存储
5使用异步IO,多路复用IO/事件驱动IO代替同步阻塞IO
6合理选择IO流
JVM配置优化方法
1合理设置各个代码的大小。新生代尽量设置大,不能过大(产生碎片),同时也要避免Survivor设置过大和过小
2选择合适的GC策略
3老年优先使用Parallel GC(-xx:+UseParallel[Old] gc),可以保证最大的吞吐量。
4注意存储墙(严重阻碍处理器性能发挥的内存瓶颈),一般讲单点应用堆内存设置为4G到5G即可,依靠可扩展性提升并发能力
5设置JVM的内存大小有个一个经验法则:完成Full Gc后,应该释放70%的内存
6配置堆内存和永生代/元空间内存之和小于32GB,从而可以使用压缩指针节省对象的指针的占用
7打开GC日志并读懂GC日志,以便排查问题,GC日志可以使用GC hIStogram(gchisto)生成图表和表格
代码性能建议
·算法,逻辑上是程序性能的首要,遇到性能问题,应该首先优化逻辑处理
·优先考虑使用返回值而不是异常表示错误,虽然JVM已做了大量优化工作,如果只关注性能,在捕获异常时,直接采用fillinstackTrace()方法为空方法,不拷贝信息
·查看自己的代码是否对内联是友好的,方法的大小不超过35个字节,非虚方法
·个人建议:使用阿里插件,会对一般错误信息不同颜色区分,绿色(可忽略),黄色(不规范),红色(错误的代码),保证代码基本正确