职业经验 你所需要掌握的问题排查知识

狂师 · 2019年03月09日 · 2143 次阅读

1. 说之前

又是很久一段时间没有在社区贡献贴子了,趁着周六在公众号中更新了一篇文章,持着分享的精神,同步更新到社区里、以尽绵薄之力。

今天要分享的内容是关于问题排查方面,由于业务应用 bug(本身或引入第三方库)、环境原因、硬件问题等原因,线上服务出现故障 / 问题几乎不可避免。例如,常见的现象包括请求超时、用户明显感受到系统发生卡顿等等。

作为一个合格的研发人员(技术人员),不仅要能写得一手好代码,掌握如何排查问题技巧也是研发人进阶必须掌握的实战技能。这里提到的排查问题不仅仅是在 Coding 的过程中 Debug,还包括测试阶段、线上发布阶段问题的排查。特别是在生产环境中,一般是没办法或很难进行 Debug 操作的。 而通过掌握服务线上问题排查思路并能够熟练排查问题常用工具 / 命令 / 平台来获取运行时的具体情况,这些运行时信息包括但不限于运行日志、异常堆栈、堆使用情况、GC 情况、JVM 参数情况、线程情况等。

排查出问题并找到根本原因加以解决,其实是一件很成就感的事情。曾经有人问过我:“你是怎么想到问题出现在 xxx 的?又是怎么确认根本原因是 xxx 的?”,我只能轻描淡写的回答:“靠经验”,其实这里说的 “靠经验” 是很模糊的,一直以来大家可能都觉得排查问题要靠经验,但是又说不出具体通过什么样的经验排查出了问题。而本质上排查定位线上问题是具有一定技巧或者说是经验规律的,排查者如果对业务系统了解得越深入,那么相对来说定位也会容易一些。排查问题的关键是什么?一句话总结:给一个系统定位排查问题的时候,知识、经验是关键,数据是依据,工具是运用知识处理数据的手段!在此,我将结合自身经历、总结,说关于 “问题排查” 的方法论,希望能与您产生更多的共鸣。

注:由于针对不同技术问题,所用到的排查工具,命令千差万别,所以本文将只介绍思路,不涉及具体排查命令的介绍。

2. 有哪些常见问题

那我们经常说遇到这样那样的问题,那到底有哪些问题,问题又集中在哪些方面?对于不同技术框架、语言族所可能引发的问题也会存在很大的差异,但基本的套路排查思路都还是一致的,以 Java 为例。

所有 Java 服务的线上问题从系统表象来看归结起来总共有四方面:CPU、内存、磁盘、网络。例如 CPU 使用率峰值突然飚高、内存溢出 (泄露)、磁盘满了、网络流量异常、FullGC 等等问题。

基于这些现象我们可以将线上问题分成两大类: 系统异常、业务服务异常。

1. 系统异常

常见的系统异常现象包括: CPU 占用率过高、CPU 上下文切换频率次数较高、磁盘满了、磁盘 I/O 过于频繁、网络流量异常 (连接数过多)、系统可用内存长期处于较低值 (导致 oom killer) 等等。

这些问题如果是在 Linux 系统下可以通过 top(cpu)、free(内存)、df(磁盘)、dstat(网络流量)、pstack、vmstat、strace(底层系统调用) 等工具获取系统异常现象数据。

注:CPU 是系统重要的监控指标,能够分析系统的整体运行状况。对 CPU 的分析或监控指标,一般包括运行队列、CPU 使用率和上下文切换等,内存是排查线上问题的重要参考依据,内存问题很多时候是引起 CPU 使用率较高的主要因素。而经常遇到内存占用飙高它的原因可能有很多。最常见的就是内存泄露。可以得到堆 dump 文件后,进行对象分析。如果有大量对象在持续被引用,并没有被释放掉,那就产生了内存泄露,就要结合代码,把不用的对象释放掉。

2. 业务服务异常

常见的业务服务异常现象包括: PV 量过高、服务调用耗时异常、线程死锁、多线程并发问题、频繁进行 Full GC、异常安全攻击扫描等。

频繁的 GC 将导致应用吞吐量下降、响应时间增加,甚至导致服务不可用。

3.问题排查方法论

一、排查问题犹如破案

排查线上问题犹如警察破案一样,是一个不停分析线索,推理的过程,但在准备排查问题之前,我们应该明白三个认知:

  • 系统出现异常是正常的

时至今日计算机系统已经变得异常复杂,一次用户请求可能要经过发送请求,DNS 解析,运营商网络,负载均衡,服务器,虚拟机(容器),视业务逻辑的复杂程度可能还要调用组件,缓存,存储和数据库等。每个环节都可能出现问题,有的组件又是分布式的,大大增加的排查问题的难度,所以出现问题后不要慌,保持好的心态。

  • 首要任务是恢复系统

飞机在发生紧急情况下,飞行员的首要任务是保持飞机飞行,相比保证乘客与飞机安全着陆,故障定位和排除是次要目标”,所以恢复线上系统是首要任务,而不是立马找到它发生的原因。

  • 真相永远只有一个

计算机是一门科学,而且计算机的世界里都是由 0 或 1 组成,在这个世界里只有是或否,没有中间地带,所以在计算机世界凡事都有根本原因,没有偶然发生,一切都是必然。正如墨菲定律所提到的 “如果事情有变坏的可能,不管这种可能性有多小,它总会发生!”

二、了解案情,评估大小

先评估出这个问题的影响范围,是全网,某些地区,还是某条链路不可用的问题,还是很多业务线都出现问题,评估出案情的大小,到底是普通的民事案件,还是刑事案件。

三、理清线索,整理分析

理清手头已得到的信息或线索,比如监控上有网络报警,有用户反馈无法访问,有开发人员反馈服务器有问题,同时间段有做变更等等,尽量不要漏掉这些看似无关紧要的线索,把这些线索先整理下来,后面一并分析。

推理的过程,就是根据已知线索,通过合理的想象、推断得出一个唯一的结果。线索是整个推理过程的起点,线索给出的好有不好、是否有错误,直接会影响推理的质量,因此是最基础、也是最重要的一环。线索的梳理,最常犯错误就是信息不足,主观臆断。

其中整理分析过程中,很重要的一点:尽可能搞清楚问题的前因后果!

不要一下子就扎到服务器前面,你需要先搞明白对这台服务器有多少已知的情况,还有故障的具体情况。不然你很可能就是在无的放矢。

必须搞清楚的问题有:

  • 故障的表现是什么?无响应?报错?

  • 故障是什么时候发现的?

  • 故障是否可重现?

  • 有没有出现的规律(比如每小时出现一次)

  • 最后一次对整个平台进行更新的内容是什么(代码、服务器等)?

  • 故障影响的特定用户群是什么样的 (已登录的, 退出的, 某个地域的…)?

  • 基础架构(物理的、逻辑的)的文档是否能找到?

  • 是否有监控平台可用?(比如 Munin、Zabbix、 Nagios、 New Relic… 什么都可以)

  • 是否有日志可以查看?.(比如 Loggly、Airbrake、 Graylog…)

另外也可以进一步从应用层、数据库层、网络层进行检查:

应用层:

  • 应用最近是否有上线?

  • 软硬件环境最近是否有变更?

  • 应用日志是否有异常?

  • 重启是否有效?

数据库:

  • 数据库系统级配置最近是否有变更?

  • telnet 端口是否畅通?

  • tnsping 监听是否正常(连通性、延迟)

  • 数据库是否有异常的等待?

  • 远程、本地 SQL 执行是否正常?

网络:

  • 网络最近是否有变更?

  • ping 是否正常?

  • traceroute -l 是否正常?

  • 网络是否有丢包、延迟?

尽可能地获取到更多的已知有效信息,汇总信息并从多条排查线去进行分析,这里推荐思路有:

  • 通过变更记录来咨询相关人员,大量问题其实都是上线、变更等引起的,所以排查下同时期有过业务相关的变更操作人员,往往就可以把很多问题排除在这道线上了。

  • 通过日志、数据等,把一些已知问题筛选出来。

  • 通过影响人群、问题点等信息尝试找出复现方法。一般来说,能有方法稳定复现的问题,就比较容易排查到了。

  • 到这一步的问题,基本上都属于一些疑难杂症了,就没有一些特别通用的方法了。需要开阔思路,找找规律,将平时没关注到的技术、业务点再了解的更细致,更深入一些,或者求助于团队的帮助一起来解决。

需要注意一点:通过分析日志时,业务日志除了要关注系统异常与业务异常之外,还要关注服务执行耗时情况,耗时过长的服务调用如果没有熔断等机制,很容易导致应用性能下降或服务不可用,服务不可用很容易导致雪崩。如果没办法直接从日志中发现异常,那就只能看看应用到底在干嘛了(可分析应用在异常时期的线程内存堆栈信息)

这一步原则很简单找出系统正在执行什么”,询问系统为什么执行这些操作以及系统的资源都被用在了哪里可以帮助你了解系统为什么出错

四、扩大你的信息量

主动扩大信息的接收面,比如问询一下开发或其它相关同学,今天有没有做线上改动,网络组有无重大调整。从中获取到有价值的信息点,对于排查问题至关重要。查看监控,细看某个监控项的变化,追踪日志和调试信息都是扩大信息量的手段。

拓展知识面,闲暇时间多些了解相关联系统,比如架构,部署,逻辑等。一旦故障发生,讨论中也可提供你解决办法的思路,举一反三,推进问题的排查与解决。

收集问题及环境信息,需要收集的信息可能有:

  • 问题的已知首次发生时间

  • 问题反馈人员所处的环境,例如省、市、ip、ISP、浏览器、手机型号、app 版本等

  • 问题是全员的还是部分的。

  • 问题发生在哪些服务器上。

  • 同期相关的日志、数据信息。

  • 同时期的上线、配置变更、运维操作、基础服务变更等记录。

  • 同时期基础服务提供商的变更、故障公告等。

五、分析证词,甄别对错

如果是外部提出的问题,比如业务投诉,用户反馈等信息,有时候是可信的,有时候人却是不可信的,举个例子之前有开发反馈效果有问题,有些广告位 bias 异常,有些正常,让我们帮查查系统的问题,但是最后是代码调用一处动态配置造成的。有些时候反馈的信息,是经过描述者过滤加工过的信息,他的排查和分析有可能把你带偏了,在收集信息同时需要以审视、怀疑的态度,分析每个人的证词。

六、看清问题本质

“当你听到蹄子声响时,应该先想到马,而不是斑马”,看到一件现象或一件事情,要看实质而不只是表面的东西,听到马蹄声时候猜是什么马,是什么人的马,是来干什么的而不是猜它是斑马还是白马还是黑马。排查问题也一样切忌先入为主,有时候你觉得极其简单,看似非常不可能发生的事情,可能就是原因,不要轻易的排除掉某项原因。例比:之前遇到有个 mysql 连接异常的问题,查了很久,做了很多调优都没有解决,最后发现是网卡跑满了。

七、确定方向,开展定位

排查步骤,可以先 “从大到小”,先看比如运营商网络,机房状态等比较宏观的地方是否有问题,逐一排除,逐步缩小问题范围。再 “从上到下”,先从现象发生的顶端调用链逐一排查,逐步向下深入。

但也并不是所有问题都从大到小从上到下,宏观问题只有达到一定量级才会引发” 质变”,从而引起的注意,在通往质变过程中,你的业务可能已经收到某中影响而表现的很明确,此时需要微观分析,然后再逐渐到宏观来诊断。

八、卷宗记录,破案归档

问题排查解决后,养成事后总结的习惯。好记性不如烂笔头,然而在一片混乱问题分析当中,心平气和地记录下问题与判断确实有点不切实际。但即使如此,我们仍然可以在事情结束后为保留一份分析资料,总结并记录处理过程中的执行步骤以及解决途径,则能帮助自己和团队积累宝贵的处理经验。

  • 对于个人

一次问题的定位解决往往伴随着个人的成长,我们不要放弃这样的机会。在追查过程中了解的知识点是比较零碎的,不系统。事后就需要大家将这些点整体串起来,并且以点带面,将知识点变更知识面。

  • 对于团队
  1. 是对这次问题的反思,我们应该在流程、代码、工具或者哪些方面做出调整,可以更好的避免同类型问题的出现。

  2. 是对追查过程的总结,在问题定位的过程中,我们缺少哪些帮助及工具的支持,能否更好的提升排查问题的效率,然后相关人员是否对过程结果存在异议。

4. 长期改进建议

吃一堑长一智出了问题并不可怕,怕的是我们从问题中学不到什么,怕的是类似的问题重现,提高问题定位的效率,有哪些值得去做,比如:

  1. 建立长效错误码机制,使用具统计、可视意义的数字来简短描述错误含义和范畴,正所谓浓缩就是精华,这一点在错误码屡试不爽。

  2. 正常程序中打错误日志主要是为了更好地排查问题和解决问题,提供重要线索和指导。但是在实际中打的错误日志内容和格式变化多样,错误提示上可能残缺不全、没有相关背景、不明其义,使得排查解决问题成为非常不方便或者耗时的操作。而实际上只要开发稍加用心,也需就会减少排查问题的很多无用功。如何编写有效的错误日志,建立日志标准,也是非常有利于问题分析的。

  3. 定位问题避免二次损害,当某个看似难以捉摸的难题出现时,本能可能是重启,尽快让系统恢复正常。虽然这样的方式经常能够解决问题而且起效神速,但同时也很可能把情况推向令人难以置信的恶化深渊。问题排查手段包括重新启动不稳定系统、尝试自动记录数据库、文件系统修复等等,这些方式往往确实能搞定难题并让系统重回生产轨道,但同时也没准导致数据恢复努力付之东流,毁掉确定问题根本原因的机会甚至大大延长关键性系统的停机时间。保留现场也非常重要,跟破案现场要要求现场勘察、样本采集、排查、锁定如出一辙,对于难以重现问题,尽量创造条件保留了可以用于故障重现的数据或现场。线上环境复杂多变,虽然这一点并不能马上解决问题起到直接作用,但坚持这种处理思路,为开发和测试创造条件,降低因难以重现的疑难故障的挂起率,最终有助于业务的长期稳定。

  4. 建立集中的数据可视平台,不至于遇到问题才开始着手分析,若是对业务没有足够的了解又没有数据依赖,就很可能在解决问题时雪上加霜。

  5. 建立沙箱影子系统,模拟复杂多变的现网环境,规避线上影响,重现或压测问题,如 tcpcopy、dubbocopy 等。

  6. 搭建开源的日志可视方案,协助我们去解决最后” 一公里” 的问题,常见如 ELK、Log.io 等。

  7. 善其事必先利其器,常见系统排查工具 perf、iptraf、netperf、tcpdump、gdb、pstack、jstack、strace,top、iotop、tsar 等。

  8. 在升级版本或者替换或修改文件时,一定要做好备份,要保证随时可以还原。

  9. 程序在使用多线程时,尽可能的减少多线程竞争锁,可以将数据分段,各个线程分别读取。

  10. 尽量不要在线程中做大量耗时的网络操作,如查询数据库(可以的话在一开始就将数据从从 DB 中查出准备好)。

  11. 建议对线程取一个有意义的名称,这样对于在排查问题时很有必要,如当拿到程序线程堆栈信息时,可直接根据线程名在日志中找到相关的堆栈。

  12. 生产环境进行问题排查时一定要保证不要影响正常的业务执行。

结尾

好吧,就先说这么多,文章同步发布到本人公众号 mikezhou_talk中,也可直接查看原文链接:点击原文链接

最近大半年都比较忙,在写一本关于自动化方面的书籍,预计可能要到下半年才能出版,书的详情暂时就不透露太多了,有兴趣的敬请关注~

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册