一般来说,只要系统架构设计得比较合理,大部分情况下系统都能正常运行,出现系统崩溃等故障问题是小概率事件。也就是说,业务开发是大部分软件工程中的重头戏,所以有人开玩笑说:“面试造火箭,入职拧螺丝。”
一般来说,我们进行排查分析的目的主要有:
我们按照问题的复杂程度,可以分为两类:
常规的问题一般在开发过程中就被发现和解决了,所以线上问题一般会比较复杂,出现在大家都没有考虑到的地方。按照我们的多年解决经验,这些复杂问题的排查方式可以分为两种途径:
如果您倾向于选择后一种方式,那么可能会浪费大量的时间,效果得看运气。更糟糕的是,因为基本靠蒙,所以这个过程是完全不可预测的,如果时间很紧张,就会在团队内部造成压力,甚至升级为甩锅和互相指责。
系统出现性能问题或者故障,究竟是不是 JVM 的问题,得从各个层面依次进行排查。
在生产环境中针对特定问题进行故障排除时,往往会有诸多限制,从而导致排查的过程变得痛苦。
1. 影响到客户的时间越短越好
面对客户的抱怨,解决问题最快的办法可能是:“只要重启机器就能让系统恢复正常”。
用最快的方法来避免对用户产生影响是很自然的需求。
但重启可能会破坏故障现场,那样就很难排查问题的根本原因了。
如果重新启动实例,则无法再采集实际发生的情况,导致我们并没有从这次故障中学习,从而获得收益。
即使重启解决了目前的问题,但问题原因本身仍然存在,一直是一个定时炸弹,还可能会接二连三地发生。
2. 安全方面的限制
接下来是安全性相关的限制,这些限制导致生产环境是独立和隔离的,一般来说,开发人员可能没有权限访问生产环境。如果没有权限访问生产环境,那就只能进行远程故障排除,并涉及到所有与之相关的问题:
特别是将临时补丁程序发布到生产环境时,“希望它能生效”,但这种试错的情况却可能导致越来越糟糕。
因为测试和发布流程可能又要消耗几小时甚至几天,进一步增加了解决问题实际消耗的时间。
如果还需要分多次上线这种“不一定生效的补丁程序”,则很可能会消耗几个星期才能解决问题。
3. 工具引发的问题
还有很重要的一点是需要使用的工具:安装使用的某些工具在特点场景下可能会使情况变得更糟。
例如:
因此,要想给系统打补丁或者增加新的远程监测程序,可能最终会花费很多天的时间:既然在生产环境中进行故障诊断排查会面临这么多的问题,很自然地,大部分情况下,我们都是在开发或测试环境中进行故障排查。
如果在开发环境或者测试环境中进行问题诊断和故障排查,则可以避免生产环境中的那些麻烦。
因为开发环境和生产环境配置不同,有些时候可能也会有问题:即很难复现生产环境中产生的 Bug 或性能问题。
例如:
这些困难会引起“这不可能,我机器上就没事” 这种很尴尬的局面。
可以看出,因为和实际的生产环境不同,所以在对某些问题进行故障排除时,当前系统环境的性质可能会让你遇到的一些莫名其妙的障碍。
除了特定环境的约束之外,还有其他方面的因素也会导致故障排除过程的不可预测性。
本节提供一些处理经验,但是我们希望他们不会成为你的应急措施(就像医生不希望你来医院)。
最好是在平时就先做好全面的系统监控、有针对性的应急预案,并时常进行演练。
能力可以分为外功和内功。内功就是各种基础知识。外功就是各种工具、技能和技巧。
分析排查问题,需要大量的专业背景知识来支撑。否则你猜都不知道怎么猜,没有方向,也就很难验证正确性了。
想要具备排查复杂问题的能力,至少得对领域行业相关的横向知识有一定的了解,最好还能对所面临的具体问题域有竖向的深刻认识和经验,即所谓的“T”字型人才。
JVM 问题排查需要掌握哪些领域相关的知识呢?下面列出了一些基础:
以及从这些领域延伸出来的各种技能和技巧。
故障排除是必不可少的过程。只要有人使用的系统,就无法避免地会发生一些故障,因此我们需要很清楚地了解系统的现状和问题。我们无法绕过不同环境带来的困扰,但也不可能“21 天就变成专家”。
在技术开发这个行业,一万小时理论总是成立的。除了累积1万小时的训练时间来成为该领域的专家之外,其实还有更快速的解决方案来减轻故障排除所带来的痛苦。
对代码进行抽样分析并没有错,尤其是在系统正式上线之前。
相反,了解应用程序各个部分的热点以及内存消耗,能有效防止某些问题影响到生产环境的用户。
虽然由于数据,使用方式和环境的差异,最终只能模拟生产环境中面临的一部分问题。 但使用这种技术可以预先进行风险排查,如果真的发生问题,可以在追溯问题原因时很快定位。
在质量保证领域投入适当的资源,尤其是自动化的持续集成、持续交付流程能及早暴露出很多问题。如果进行周全和彻底的测试,将进一步减少生产环境的事故。但是,这些工作往往得不到资源,“你看功能不是已经完成了吗,为什么还要去花人力物力继续搞,又没有收益”。
实际的工作中,我们很难证明对质量检查的投资是否合理。
一切标有“性能测试”或“验收测试”的产品,最终都将与清晰而可衡量的业务目标(新功能开发)存在竞争。
现在,当开发人员推动“执行某项性能优化”的任务时,如果不能提升优先级,此类任务会积压下来,永远都是待办事项。
为了证明这种投资的合理性,我们需要将投资回报与质量联系起来。如果将生产环境中的P1故障事件减少到80%,可以让我们多产生2倍的收益,在这种情况下,我们就能推动相关人员把这些工作做好。相反地,如果我们不能说明我们改进工作的收益,则我们可能就没有资源去提升质量。
曾经有一个特别形象的例子:农村的孩子为什么不读书?
家长说:因为穷,所以不读书。
那又为什么而贫穷呢?
家长说:因为不读书,所以穷。
系统就像是人体一样,只要活着就会在某个时间点生病,所以,我们必须接受的第一件事情就是生产环境一定会出现问题,不管是 Bug 引起的,还是人为操作失误,亦或者天灾导致的。
即使是 NASA/SpaceX 这种高端组织也会不时地炸几艘飞船火箭,因此我们需要为线上发生的问题做好准备。
无论怎么分析和测试,总会有些漏掉的地方,事故就在这些地方产生。
既然无法避免总会需要对生产环境进行故障排除。为了更好地完成手头的工作,就需要监控生产环境中系统的状态。
不幸的是,监控领域并没有万能的银弹。即使是最新的技术也无法提供不同场景下的所有信息。
典型的 Web 应用系统,至少应该集成下面这些部分:
确保在系统发布之前已经在开发环境中进行过系统性能分析,并经过测试验收,从而减少生产故障。
了解生产部署环境并做好监控,当故障发生时,我们就能可预料的方式,更快地做出响应。
前面的小节讲了一般的问题诊断和调优套路:
做好监控,定位问题,验证结果,总结归纳。
下面我们看看 JVM 领域有哪些问题.
从上图可以看到,JVM 可以划分为这些部分:
对这些有了了解,在我们进行知识储备时,就可以按照自顶向下的方式,逐个攻克。
线上环境的JVM问题主要集中在 GC 和内存部分。而栈内存、线程分析等问题,主要是辅助诊断 Java 程序本身的问题。
相关的知识点,如果有不清楚的地方,请各位同学回过头来,再读一读前面的章节。
我认为这些基础的技术和知识,需要阅读和练习 2~3 遍,才会掌握的比较牢固;毕竟理解和掌握了才是自己的。
有读者朋友提问:
我希望能在课程中总结一下,JVM 的参数设置,应该按照哪些步骤来设置?
截止目前(2020 年 3 月),JVM 可配置参数已经达到 1000 多个,其中 GC 和内存配置相关的 JVM 参数就有 600 多个。从这个参数比例也可以看出,JVM 问题排查和性能调优的重点领域还是 GC 和内存。
参数太多是个大麻烦,让人难以下手,学习和理解起来也很费事。
但在绝大部分业务场景下,常用的 JVM 配置参数也就 10 来个。
我们先给一个示例,读者可根据需要适当增减。
# 设置堆内存
-Xmx4g -Xms4g
# 指定 GC 算法
-XX:+UseG1GC -XX:MaxGCPauseMillis=50
# 指定 GC 并行线程数
-XX:ParallelGCThreads=4
# 打印 GC 日志
-XX:+PrintGCDetails -XX:+PrintGCDateStamps
# 指定 GC 日志文件
-Xloggc:gc.log
# 指定 Meta 区的最大值
-XX:MaxMetaspaceSize=2g
# 设置单个线程栈的大小
-Xss1m
# 指定堆内存溢出时自动进行 Dump
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/usr/local/
这些参数我们在前面的章节中都介绍过。
此外,还有一些常用的属性配置:
# 指定默认的连接超时时间
-Dsun.net.client.defaultConnectTimeout=2000
-Dsun.net.client.defaultReadTimeout=2000
# 指定时区
-Duser.timezone=GMT+08
# 设置默认的文件编码为 UTF-8
-Dfile.encoding=UTF-8
# 指定随机数熵源(Entropy Source)
-Djava.security.egd=file:/dev/./urandom
如果使用自己熟悉的工具,并且对于故障排除的规则已经胸有成竹,那么环境限制就不再是什么大问题。
实际上,负责排查和解决问题的工程师通常没有预先规划好的处理流程。
老实说,大家是否有过像下面这样的 shell 操作:
# 查看当前路径
pwd
# 查看当前目录下有哪些文件
ls -l
# 查看系统负载
top
# 查看剩余内存
free -h
# 查看剩余磁盘
df -h
# 查看当前目录的使用量
du -sh *
# 系统活动情况报告
sar
-bash: sar: command not found
# Linux安装sysstat
# apt-get install sysstat
# yum -y install sysstat
# 查看帮助手册
man sar
# 查看最近的报告
sar 1
# ???
sar -G 1 3
sar: illegal option -- G
# 查看帮助手册
man sar
# ......
如果您觉得上面这个过程很熟悉,别紧张,其实大家都这样干。
大多数工程师在故障排除和诊断调优领域都缺乏经验,因此也就很难使用标准的套路。
这没什么丢人的——除非是 Brendan Gregg or Peter Lawrey 这种专业大牛,否则您很难有 1 万小时的故障排除经历,也就很难成为这个领域的专家。
缺乏经验的话,针对当前问题,往往需要使用不同的工具来收集信息,例如:
最容易排查的是系统硬件和操作系统问题,比如:CPU、内存、网络、磁盘 IO。
我们可以使用的工具几乎是无限的。与实际解决问题相比,使用各种不熟悉的工具,可能会浪费更多的时间。
回顾一下,我们的课程介绍了可量化的 3 个性能指标:
也可以具体拓展到单机并发,总体并发、数据量;用户数、预算成本等等。
不同的场景、不同的问题,排查方式都不一样,具体怎么来确定问题是没有固定套路的。
可以事先进行的操作包括:
各家公司可能有自己的事故处理规范,可能会涉及这些因素:
\1. 查询业务日志,可以发现这类问题:请求压力大、波峰、遭遇降级、熔断等等,基础服务、外部 API 依赖。
\2. 查看系统资源和监控信息:
\3. 查看性能指标,包括实时监控、历史数据。可以发现假死、卡顿、响应变慢等现象。
\4. 排查系统日志,比如重启、崩溃、Kill。
\5. APM,比如发现有些链路请求变慢等等。
\6. 排查应用系统:
\7. 排除资源竞争、坏邻居效应。
\8. 疑难问题排查分析手段:
总之,时至今日,软件领域的快速发展,使得我们可以使用的手段和工具,都很丰富。勤学苦练,掌握一些常见的套路,熟练搭配应用一些工具,是我们技术成长,能快速解决问题的不二法门。
© 2019 - 2023 Liangliang Lee. Powered by gin and hexo-theme-book.